this repo has no description

Compare changes

Choose any two refs to compare.

Changed files
+8049 -4211
.github
.vscode
nix
packages
browser
core
core-extensions
injector
node-preload
types
web-preload
scripts
-75
.eslintrc.json
··· 1 - { 2 - "root": true, 3 - "extends": [ 4 - "eslint:recommended", 5 - "plugin:@typescript-eslint/recommended", 6 - "plugin:prettier/recommended", 7 - "plugin:react/recommended" 8 - ], 9 - "plugins": ["@typescript-eslint", "prettier", "react"], 10 - "parser": "@typescript-eslint/parser", 11 - "env": { 12 - "browser": true, 13 - "node": true 14 - }, 15 - "parserOptions": { 16 - "ecmaFeatures": { 17 - "jsx": true 18 - }, 19 - "ecmaVersion": "latest", 20 - "sourceType": "module" 21 - }, 22 - "rules": { 23 - "indent": "off", 24 - "eqeqeq": [ 25 - "error", 26 - "always", 27 - { 28 - "null": "ignore" 29 - } 30 - ], 31 - "quotes": [ 32 - "error", 33 - "double", 34 - { "avoidEscape": true, "allowTemplateLiterals": true } 35 - ], 36 - "@typescript-eslint/no-unused-vars": [ 37 - "error", 38 - { "args": "none", "varsIgnorePattern": "^_" } 39 - ], 40 - // Mostly so we don't forget to leave these in when committing 41 - "no-console": "error", 42 - "no-debugger": "error", 43 - 44 - // Quite honestly we're interacting with so much unknown within Discord that 45 - // this being enabled is a hinderance 46 - "@typescript-eslint/no-explicit-any": "off", 47 - 48 - "@typescript-eslint/no-var-requires": "off", 49 - 50 - // https://canary.discord.com/channels/1154257010532032512/1154275441788583996/1181760413231230976 51 - "no-unused-labels": "off", 52 - 53 - // baseUrl being set to ./packages/ makes language server suggest "types/src" instead of "@moonlight-mod/types" 54 - "no-restricted-imports": [ 55 - "error", 56 - { 57 - "patterns": [ 58 - { 59 - "group": ["types/*"], 60 - "message": "Use @moonlight-mod/types instead" 61 - }, 62 - { 63 - "group": ["core/*"], 64 - "message": "Use @moonlight-mod/core instead" 65 - } 66 - ] 67 - } 68 - ] 69 - }, 70 - "settings": { 71 - "react": { 72 - "version": "18.2" 73 - } 74 - } 75 - }
+4 -8
.github/workflows/browser.yml
··· 10 10 name: Browser extension builds 11 11 runs-on: ubuntu-latest 12 12 steps: 13 - - uses: actions/checkout@v3 14 - 15 - - uses: pnpm/action-setup@v2 16 - with: 17 - version: 9 18 - run_install: false 19 - - uses: actions/setup-node@v3 13 + - uses: actions/checkout@v4 14 + - uses: pnpm/action-setup@v4 15 + - uses: actions/setup-node@v4 20 16 with: 21 - node-version: 18 17 + node-version: 22 22 18 cache: pnpm 23 19 24 20 - name: Install dependencies
+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
+7 -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 ··· 47 43 echo "$(date +%s)" >> ./dist/ref 48 44 49 45 - name: Setup GitHub Pages 50 - uses: actions/configure-pages@v3 46 + uses: actions/configure-pages@v5 51 47 - name: Upload artifact 52 - uses: actions/upload-pages-artifact@v1 48 + uses: actions/upload-pages-artifact@v3 53 49 with: 54 50 path: ./dist 55 51 - name: Deploy to GitHub Pages 56 - uses: actions/deploy-pages@v2 52 + uses: actions/deploy-pages@v4
+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
+4 -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
+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
··· 3 3 dist.tar.gz 4 4 .DS_Store 5 5 eslint_report.json 6 - 6 + .eslintcache 7 7 # Nix 8 8 /result 9 9 *.drv 10 + 11 + # IDEs 12 + .vscode/ 13 + .idea/
+4 -4
.prettierrc
··· 1 1 { 2 - "printWidth": 80, 3 - "trailingComma": "none", 4 - "tabWidth": 2, 5 - "singleQuote": false 2 + "printWidth": 120, 3 + "trailingComma": "none", 4 + "tabWidth": 2, 5 + "singleQuote": false 6 6 }
-14
.vscode/tasks.json
··· 1 - { 2 - "version": "2.0.0", 3 - "tasks": [ 4 - { 5 - "label": "build", 6 - "type": "shell", 7 - "command": "pnpm run build", 8 - "group": { 9 - "kind": "build", 10 - "isDefault": true 11 - } 12 - } 13 - ] 14 - }
+4 -1
CHANGELOG.md
··· 1 - - core-extensions/contextMenu: Fix patches 1 + ## Core 2 + 3 + - Updated mappings 4 + - Fixed using remapped paths as patch finds not working
+10 -3
README.md
··· 5 5 <img src="./img/wordmark.png" alt="moonlight" /> 6 6 </picture> 7 7 8 - <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> 9 11 \- <a href="https://github.com/moonlight-mod/moonlight">GitHub</a> 10 - \- <a href="https://moonlight-mod.github.io/">Docs</a> 11 12 12 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> 13 20 </h3> 14 21 15 22 **moonlight** is yet another Discord client mod, focused on providing a decent user and developer experience. 16 23 17 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. 18 25 19 - **_This is an experimental passion project._** Anything and everything is subject to change, but it is stable enough for developers to experiment with. 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. 20 27 21 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.
+34 -44
build.mjs
··· 15 15 const watch = process.argv.includes("--watch"); 16 16 const browser = process.argv.includes("--browser"); 17 17 const mv2 = process.argv.includes("--mv2"); 18 + const clean = process.argv.includes("--clean"); 18 19 19 20 const buildBranch = process.env.MOONLIGHT_BRANCH ?? "dev"; 20 21 const buildVersion = process.env.MOONLIGHT_VERSION ?? "dev"; ··· 69 70 name: "build-log", 70 71 setup(build) { 71 72 build.onEnd((result) => { 72 - console.log( 73 - `[${timeFormatter.format(new Date())}] [${tag}] build finished` 74 - ); 73 + console.log(`[${timeFormatter.format(new Date())}] [${tag}] build finished`); 75 74 }); 76 75 } 77 76 }); ··· 104 103 MOONLIGHT_VERSION: `"${buildVersion}"` 105 104 }; 106 105 107 - for (const iterName of [ 108 - "injector", 109 - "node-preload", 110 - "web-preload", 111 - "browser" 112 - ]) { 106 + for (const iterName of ["injector", "node-preload", "web-preload", "browser"]) { 113 107 const snake = iterName.replace(/-/g, "_").toUpperCase(); 114 108 define[`MOONLIGHT_${snake}`] = (name === iterName).toString(); 115 109 } ··· 121 115 if (name === "browser") { 122 116 plugins.push( 123 117 copyStaticFiles({ 124 - src: mv2 125 - ? "./packages/browser/manifestv2.json" 126 - : "./packages/browser/manifest.json", 118 + src: mv2 ? "./packages/browser/manifestv2.json" : "./packages/browser/manifest.json", 127 119 dest: `./dist/${browserDir}/manifest.json` 128 120 }) 129 121 ); ··· 145 137 146 138 plugins.push( 147 139 copyStaticFiles({ 148 - src: mv2 149 - ? "./packages/browser/src/background-mv2.js" 150 - : "./packages/browser/src/background.js", 140 + src: mv2 ? "./packages/browser/src/background-mv2.js" : "./packages/browser/src/background.js", 151 141 dest: `./dist/${browserDir}/background.js` 152 142 }) 153 143 ); ··· 174 164 dropLabels, 175 165 176 166 logLevel: "silent", 177 - plugins 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 178 176 }; 179 177 180 178 if (name === "browser") { 181 179 const coreExtensionsJson = {}; 182 180 183 - // eslint-disable-next-line no-inner-declarations 184 181 function readDir(dir) { 185 182 const files = fs.readdirSync(dir); 186 183 for (const file of files) { ··· 189 186 if (fs.statSync(filePath).isDirectory()) { 190 187 readDir(filePath); 191 188 } else { 192 - coreExtensionsJson[normalizedPath] = fs.readFileSync( 193 - filePath, 194 - "utf8" 195 - ); 189 + coreExtensionsJson[normalizedPath] = fs.readFileSync(filePath, "utf8"); 196 190 } 197 191 } 198 192 } ··· 200 194 readDir("./dist/core-extensions"); 201 195 202 196 esbuildConfig.banner = { 203 - js: `window._moonlight_coreExtensionsStr = ${JSON.stringify( 204 - JSON.stringify(coreExtensionsJson) 205 - )};` 197 + js: `window._moonlight_coreExtensionsStr = ${JSON.stringify(JSON.stringify(coreExtensionsJson))};` 206 198 }; 207 199 } 208 200 ··· 214 206 } 215 207 } 216 208 217 - async function buildExt(ext, side, copyManifest, fileExt) { 209 + async function buildExt(ext, side, fileExt) { 218 210 const outdir = path.join("./dist", "core-extensions", ext); 219 211 if (!fs.existsSync(outdir)) { 220 212 fs.mkdirSync(outdir, { recursive: true }); 221 213 } 222 214 223 - const entryPoints = [ 224 - `packages/core-extensions/src/${ext}/${side}.${fileExt}` 225 - ]; 215 + const entryPoints = [`packages/core-extensions/src/${ext}/${side}.${fileExt}`]; 226 216 227 217 const wpModulesDir = `packages/core-extensions/src/${ext}/webpackModules`; 228 218 if (fs.existsSync(wpModulesDir) && side === "index") { 229 219 const wpModules = fs.opendirSync(wpModulesDir); 230 220 for await (const wpModule of wpModules) { 231 221 if (wpModule.isFile()) { 232 - entryPoints.push( 233 - `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}` 234 - ); 222 + entryPoints.push(`packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}`); 235 223 } else { 236 224 for (const fileExt of ["ts", "tsx"]) { 237 225 const path = `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}/index.${fileExt}`; ··· 259 247 } 260 248 }; 261 249 250 + const styleInput = `packages/core-extensions/src/${ext}/style.css`; 251 + const styleOutput = `dist/core-extensions/${ext}/style.css`; 252 + 262 253 const esbuildConfig = { 263 254 entryPoints, 264 255 outdir, ··· 278 269 }, 279 270 logLevel: "silent", 280 271 plugins: [ 281 - ...(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) 282 277 ? [ 283 278 copyStaticFiles({ 284 - src: `./packages/core-extensions/src/${ext}/manifest.json`, 285 - dest: `./dist/core-extensions/${ext}/manifest.json` 279 + src: styleInput, 280 + dest: styleOutput 286 281 }) 287 282 ] 288 283 : []), ··· 302 297 303 298 const promises = []; 304 299 305 - if (browser) { 300 + if (clean) { 301 + fs.rmSync("./dist", { recursive: true, force: true }); 302 + } else if (browser) { 306 303 build("browser", "packages/browser/src/index.ts"); 307 304 } else { 308 305 for (const [name, entry] of Object.entries(config)) { ··· 311 308 312 309 const coreExtensions = fs.readdirSync("./packages/core-extensions/src"); 313 310 for (const ext of coreExtensions) { 314 - let copiedManifest = false; 315 - 316 311 for (const fileExt of ["ts", "tsx"]) { 317 312 for (const type of ["index", "node", "host"]) { 318 - if ( 319 - fs.existsSync( 320 - `./packages/core-extensions/src/${ext}/${type}.${fileExt}` 321 - ) 322 - ) { 323 - promises.push(buildExt(ext, type, !copiedManifest, fileExt)); 324 - copiedManifest = true; 313 + if (fs.existsSync(`./packages/core-extensions/src/${ext}/${type}.${fileExt}`)) { 314 + promises.push(buildExt(ext, type, fileExt)); 325 315 } 326 316 } 327 317 }
+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
··· 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": 1728067476, 42 - "narHash": "sha256-/uJcVXuBt+VFCPQIX+4YnYrHaubJSx4HoNsJVNRgANM=", 23 + "lastModified": 1744232761, 24 + "narHash": "sha256-gbl9hE39nQRpZaLjhWKmEu5ejtQsgI5TWYrIVVJn30U=", 43 25 "owner": "NixOS", 44 26 "repo": "nixpkgs", 45 - "rev": "6e6b3dd395c3b1eb9be9f2d096383a8d05add030", 27 + "rev": "f675531bc7e6657c10a18b565cfebd8aa9e24c14", 46 28 "type": "github" 47 29 }, 48 30 "original": { 49 31 "owner": "NixOS", 50 - "ref": "nixos-24.05", 51 - "repo": "nixpkgs", 52 - "type": "github" 53 - } 54 - }, 55 - "nixpkgs_2": { 56 - "locked": { 57 - "lastModified": 1727802920, 58 - "narHash": "sha256-HP89HZOT0ReIbI7IJZJQoJgxvB2Tn28V6XS3MNKnfLs=", 59 - "owner": "nixos", 60 - "repo": "nixpkgs", 61 - "rev": "27e30d177e57d912d614c88c622dcfdb2e6e6515", 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": 1728137762, 78 - "narHash": "sha256-iEFvPR3BopGyI5KjQ1DK+gEZ1dKDugq838tKdet2moQ=", 79 - "owner": "NotNite", 80 - "repo": "pnpm2nix-nzbr", 81 - "rev": "b7a60d3c7d106b601665e3f05dba6cdc6f59f959", 82 - "type": "github" 83 - }, 84 - "original": { 85 - "owner": "NotNite", 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=",
+3 -4
flake.nix
··· 2 2 description = "Yet another Discord mod"; 3 3 4 4 inputs = { 5 - nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; 5 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 6 flake-utils.url = "github:numtide/flake-utils"; 7 - pnpm2nix.url = "github:NotNite/pnpm2nix-nzbr"; 8 7 }; 9 8 10 - outputs = { self, nixpkgs, flake-utils, pnpm2nix }: 11 - let overlay = import ./nix/overlay.nix { inherit pnpm2nix; }; 9 + outputs = { self, nixpkgs, flake-utils }: 10 + let overlay = import ./nix/overlay.nix { }; 12 11 in flake-utils.lib.eachDefaultSystem (system: 13 12 let 14 13 pkgs = import nixpkgs {
+46 -17
nix/default.nix
··· 1 - { pkgs, mkPnpmPackage }: 1 + { 2 + lib, 3 + stdenv, 4 + nodejs_22, 5 + pnpm_10, 6 + }: 2 7 3 - mkPnpmPackage rec { 4 - workspace = ./..; 8 + stdenv.mkDerivation (finalAttrs: { 9 + pname = "moonlight"; 10 + version = (builtins.fromJSON (builtins.readFile ./../package.json)).version; 11 + 5 12 src = ./..; 6 13 7 - # Work around a bug with how it expects dist 8 - components = [ 9 - "packages/core" 10 - "packages/core-extensions" 11 - "packages/injector" 12 - "packages/node-preload" 13 - "packages/types" 14 - "packages/web-preload" 14 + outputs = [ "out" "firefox" ]; 15 + 16 + nativeBuildInputs = [ 17 + nodejs_22 18 + pnpm_10.configHook 15 19 ]; 16 - distDirs = [ "dist" ]; 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 17 42 18 - copyNodeModules = true; 19 - buildPhase = "pnpm run build"; 20 - installPhase = "cp -r dist $out"; 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 + ''; 21 50 22 - meta = with pkgs.lib; { 51 + meta = with lib; { 23 52 description = "Yet another Discord mod"; 24 53 homepage = "https://moonlight-mod.github.io/"; 25 54 license = licenses.lgpl3; 26 55 maintainers = with maintainers; [ notnite ]; 27 56 }; 28 - } 57 + })
+3 -6
nix/overlay.nix
··· 1 - { pnpm2nix }: 1 + { ... }: 2 2 3 3 let 4 4 nameTable = { ··· 29 29 ''; 30 30 31 31 packageJson = '' 32 - {"name":"discord","main":"./injector.js","private":true} 32 + {"name":"${name}","main":"./injector.js","private":true} 33 33 ''; 34 34 35 35 in old.installPhase + "\n" + '' ··· 49 49 ''; 50 50 }); 51 51 in final: prev: rec { 52 - moonlight-mod = final.callPackage ./default.nix { 53 - pkgs = final; 54 - mkPnpmPackage = pnpm2nix.packages.${final.system}.mkPnpmPackage; 55 - }; 52 + moonlight-mod = final.callPackage ./default.nix { }; 56 53 discord = mkOverride prev moonlight-mod "discord"; 57 54 discord-ptb = mkOverride prev moonlight-mod "discord-ptb"; 58 55 discord-canary = mkOverride prev moonlight-mod "discord-canary";
+24 -16
package.json
··· 1 1 { 2 2 "name": "moonlight", 3 - "version": "1.2.1", 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", 17 26 "browser": "node build.mjs --browser", 18 27 "browser-mv2": "node build.mjs --browser --mv2", 19 28 "lint": "eslint packages", 20 - "lint:fix": "eslint packages", 21 - "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", 22 31 "typecheck": "tsc --noEmit", 23 32 "check": "pnpm run lint && pnpm run typecheck", 24 - "prepare": "husky install" 33 + "prepare": "husky install", 34 + "updates": "pnpm taze -r" 25 35 }, 26 36 "devDependencies": { 27 - "@typescript-eslint/eslint-plugin": "^6.13.2", 28 - "@typescript-eslint/parser": "^6.13.2", 29 - "esbuild": "^0.19.3", 30 - "esbuild-copy-static-files": "^0.1.0", 31 - "eslint": "^8.55.0", 32 - "eslint-config-prettier": "^9.1.0", 33 - "eslint-plugin-prettier": "^5.0.1", 34 - "eslint-plugin-react": "^7.33.2", 35 - "husky": "^8.0.3", 36 - "prettier": "^3.1.0", 37 - "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" 38 46 } 39 47 }
+2 -1
packages/browser/blockLoading.json
··· 6 6 "type": "block" 7 7 }, 8 8 "condition": { 9 - "urlFilter": "*://discord.com/assets/*.js", 9 + "requestDomains": ["discord.com", "discordapp.com"], 10 + "urlFilter": "*/assets/*.js", 10 11 "resourceTypes": ["script"] 11 12 } 12 13 }
+7 -10
packages/browser/manifest.json
··· 1 1 { 2 + "$schema": "https://json.schemastore.org/chrome-manifest", 2 3 "manifest_version": 3, 3 4 "name": "moonlight", 4 5 "description": "Yet another Discord mod", 5 - "version": "1.1.0", 6 - "permissions": [ 7 - "declarativeNetRequestWithHostAccess", 8 - "webRequest", 9 - "scripting", 10 - "webNavigation" 11 - ], 6 + "version": "1.3.14", 7 + "permissions": ["declarativeNetRequestWithHostAccess", "webRequest", "scripting", "webNavigation"], 12 8 "host_permissions": [ 13 9 "https://moonlight-mod.github.io/*", 14 10 "https://api.github.com/*", 15 - "https://*.discord.com/*" 11 + "https://*.discord.com/*", 12 + "https://*.discordapp.com/*" 16 13 ], 17 14 "content_scripts": [ 18 15 { 19 16 "js": ["index.js"], 20 - "matches": ["https://*.discord.com/*"], 17 + "matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"], 21 18 "run_at": "document_start", 22 19 "world": "MAIN" 23 20 } ··· 43 40 "web_accessible_resources": [ 44 41 { 45 42 "resources": ["index.js"], 46 - "matches": ["https://*.discord.com/*"] 43 + "matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"] 47 44 } 48 45 ] 49 46 }
+12 -6
packages/browser/manifestv2.json
··· 1 1 { 2 + "$schema": "https://json.schemastore.org/chrome-manifest", 2 3 "manifest_version": 2, 3 4 "name": "moonlight", 4 5 "description": "Yet another Discord mod", 5 - "version": "1.1.0", 6 + "version": "1.3.14", 6 7 "permissions": [ 7 8 "webRequest", 8 9 "webRequestBlocking", 9 10 "scripting", 10 11 "webNavigation", 11 - "https://*.discord.com/assets/*.js", 12 + "https://*.discord.com/*", 13 + "https://*.discordapp.com/*", 12 14 "https://moonlight-mod.github.io/*", 13 - "https://api.github.com/*", 14 - "https://*.discord.com/*" 15 + "https://api.github.com/*" 15 16 ], 16 17 "background": { 17 18 "scripts": ["background.js"] ··· 19 20 "content_scripts": [ 20 21 { 21 22 "js": ["index.js"], 22 - "matches": ["https://*.discord.com/*"], 23 + "matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"], 23 24 "run_at": "document_start", 24 25 "world": "MAIN" 25 26 } 26 - ] 27 + ], 28 + "browser_specific_settings": { 29 + "gecko": { 30 + "id": "{0fb6d66f-f22d-4555-a87b-34ef4bea5e2a}" 31 + } 32 + } 27 33 }
+1 -1
packages/browser/modifyResponseHeaders.json
··· 13 13 }, 14 14 "condition": { 15 15 "resourceTypes": ["main_frame"], 16 - "initiatorDomains": ["discord.com"] 16 + "requestDomains": ["discord.com"] 17 17 } 18 18 } 19 19 ]
+12 -2
packages/browser/package.json
··· 1 1 { 2 2 "name": "@moonlight-mod/browser", 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 "@moonlight-mod/web-preload": "workspace:*", 8 - "@zenfs/core": "^1.0.2", 9 - "@zenfs/dom": "^0.2.16" 14 + "@zenfs/core": "catalog:prod", 15 + "@zenfs/dom": "catalog:prod" 16 + }, 17 + "engineStrict": true, 18 + "devDependencies": { 19 + "@types/chrome": "catalog:dev" 10 20 } 11 21 }
+55 -70
packages/browser/src/background-mv2.js
··· 1 1 /* eslint-disable no-console */ 2 2 /* eslint-disable no-undef */ 3 3 4 - const starterUrls = ["web.", "sentry."]; 5 - let blockLoading = true; 6 - let doing = false; 7 - let collectedUrls = new Set(); 4 + const scriptUrls = ["web.", "sentry."]; 5 + let blockedScripts = new Set(); 8 6 9 - chrome.webNavigation.onBeforeNavigate.addListener(async (details) => { 10 - const url = new URL(details.url); 11 - if (!blockLoading && url.hostname.endsWith("discord.com")) { 12 - console.log("Blocking", details.url); 13 - blockLoading = true; 14 - collectedUrls.clear(); 15 - } 16 - }); 7 + chrome.webRequest.onBeforeRequest.addListener( 8 + async (details) => { 9 + if (details.tabId === -1) return; 17 10 18 - async function doTheThing(urls, tabId) { 19 - console.log("Doing", urls, tabId); 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 20 21 - blockLoading = false; 21 + if (blockedScripts.size === scriptUrls.length) { 22 + const blockedScriptsCopy = Array.from(blockedScripts); 23 + blockedScripts.clear(); 22 24 23 - try { 24 - await chrome.scripting.executeScript({ 25 - target: { tabId }, 26 - world: "MAIN", 27 - args: [urls], 28 - func: async (urls) => { 29 - try { 30 - await window._moonlightBrowserInit(); 31 - } catch (e) { 32 - console.log(e); 33 - } 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 + } 34 38 35 - const scripts = [...document.querySelectorAll("script")].filter( 36 - (script) => script.src && urls.some((url) => url.includes(script.src)) 37 - ); 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 + ); 38 44 39 - // backwards 40 - urls.reverse(); 41 - for (const url of urls) { 42 - const script = scripts.find((script) => url.includes(script.src)); 43 - console.log("adding new script", script); 45 + blockedScripts.reverse(); 46 + for (const url of blockedScripts) { 47 + if (url.includes("/sentry.")) continue; 44 48 45 - const newScript = document.createElement("script"); 46 - for (const { name, value } of script.attributes) { 47 - newScript.setAttribute(name, value); 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 + } 48 61 } 49 - 50 - script.remove(); 51 - document.documentElement.appendChild(newScript); 52 - } 53 - } 54 - }); 55 - } catch (e) { 56 - console.log(e); 57 - } 58 - 59 - doing = false; 60 - collectedUrls.clear(); 61 - } 62 - 63 - chrome.webRequest.onBeforeRequest.addListener( 64 - async (details) => { 65 - if (starterUrls.some((url) => details.url.includes(url))) { 66 - console.log("Adding", details.url); 67 - collectedUrls.add(details.url); 62 + }); 63 + }, 0); 68 64 } 69 65 70 - if (collectedUrls.size === starterUrls.length) { 71 - if (doing) return; 72 - if (!blockLoading) return; 73 - doing = true; 74 - const urls = [...collectedUrls]; 75 - const tabId = details.tabId; 76 - 77 - // yes this is a load-bearing sleep 78 - setTimeout(() => doTheThing(urls, tabId), 0); 79 - } 80 - 81 - if (blockLoading) return { cancel: true }; 66 + if (hasUrl) return { cancel: true }; 82 67 }, 83 68 { 84 - urls: ["https://*.discord.com/assets/*.js"] 69 + urls: ["https://*.discord.com/assets/*.js", "https://*.discordapp.com/assets/*.js"] 85 70 }, 86 71 ["blocking"] 87 72 ); ··· 94 79 ) 95 80 }; 96 81 }, 97 - { urls: ["https://*.discord.com/*"] }, 82 + { urls: ["https://*.discord.com/*", "https://*.discordapp.com/*"] }, 98 83 ["blocking", "responseHeaders"] 99 84 );
+37 -40
packages/browser/src/background.js
··· 1 1 /* eslint-disable no-console */ 2 2 /* eslint-disable no-undef */ 3 3 4 - const starterUrls = ["web.", "sentry."]; 5 - let blockLoading = true; 6 - let doing = false; 7 - let collectedUrls = new Set(); 4 + const scriptUrls = ["web.", "sentry."]; 5 + let blockedScripts = new Set(); 8 6 9 7 chrome.webNavigation.onBeforeNavigate.addListener(async (details) => { 10 8 const url = new URL(details.url); 11 - if (!blockLoading && url.hostname.endsWith("discord.com")) { 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"); 12 14 await chrome.declarativeNetRequest.updateEnabledRulesets({ 13 15 enableRulesetIds: ["modifyResponseHeaders", "blockLoading"] 14 16 }); 15 - blockLoading = true; 16 - collectedUrls.clear(); 17 17 } 18 18 }); 19 19 20 20 chrome.webRequest.onBeforeRequest.addListener( 21 21 async (details) => { 22 22 if (details.tabId === -1) return; 23 - if (starterUrls.some((url) => details.url.includes(url))) { 24 - console.log("Adding", details.url); 25 - collectedUrls.add(details.url); 26 - } 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 + }); 27 32 28 - if (collectedUrls.size === starterUrls.length) { 29 - if (doing) return; 30 - if (!blockLoading) return; 31 - doing = true; 32 - const urls = [...collectedUrls]; 33 - console.log("Doing", urls); 33 + if (hasUrl) blockedScripts.add(details.url); 34 + 35 + if (blockedScripts.size === scriptUrls.length) { 36 + const blockedScriptsCopy = Array.from(blockedScripts); 37 + blockedScripts.clear(); 34 38 35 39 console.log("Running moonlight script"); 36 40 try { ··· 40 44 files: ["index.js"] 41 45 }); 42 46 } catch (e) { 43 - console.log(e); 47 + console.error(e); 44 48 } 45 49 46 50 console.log("Initializing moonlight"); ··· 52 56 try { 53 57 await window._moonlightBrowserInit(); 54 58 } catch (e) { 55 - console.log(e); 59 + console.error(e); 56 60 } 57 61 } 58 62 }); ··· 60 64 console.log(e); 61 65 } 62 66 63 - console.log("Updating rulesets"); 67 + console.log("Disabling block ruleset"); 64 68 try { 65 - blockLoading = false; 66 69 await chrome.declarativeNetRequest.updateEnabledRulesets({ 67 70 disableRulesetIds: ["blockLoading"], 68 71 enableRulesetIds: ["modifyResponseHeaders"] 69 72 }); 70 73 } catch (e) { 71 - console.log(e); 74 + console.error(e); 72 75 } 73 76 74 77 console.log("Readding scripts"); ··· 76 79 await chrome.scripting.executeScript({ 77 80 target: { tabId: details.tabId }, 78 81 world: "MAIN", 79 - args: [urls], 80 - func: async (urls) => { 82 + args: [blockedScriptsCopy], 83 + func: async (blockedScripts) => { 81 84 const scripts = [...document.querySelectorAll("script")].filter( 82 - (script) => 83 - script.src && urls.some((url) => url.includes(script.src)) 85 + (script) => script.src && blockedScripts.some((url) => url.includes(script.src)) 84 86 ); 85 87 86 - // backwards 87 - urls.reverse(); 88 - for (const url of urls) { 89 - const script = scripts.find((script) => url.includes(script.src)); 90 - console.log("adding new script", script); 88 + blockedScripts.reverse(); 89 + for (const url of blockedScripts) { 90 + if (url.includes("/sentry.")) continue; 91 91 92 + const script = scripts.find((script) => url.includes(script.src)); 92 93 const newScript = document.createElement("script"); 93 - for (const { name, value } of script.attributes) { 94 - newScript.setAttribute(name, value); 94 + for (const attr of script.attributes) { 95 + if (attr.name === "src") attr.value += "?inj"; 96 + newScript.setAttribute(attr.name, attr.value); 95 97 } 96 - 97 98 script.remove(); 98 99 document.documentElement.appendChild(newScript); 99 100 } 100 101 } 101 102 }); 102 103 } catch (e) { 103 - console.log(e); 104 + console.error(e); 104 105 } 105 - 106 - console.log("Done"); 107 - doing = false; 108 - collectedUrls.clear(); 109 106 } 110 107 }, 111 108 { 112 - urls: ["*://*.discord.com/assets/*.js"] 109 + urls: ["*://*.discord.com/assets/*.js", "*://*.discordapp.com/assets/*.js"] 113 110 } 114 111 );
+97 -81
packages/browser/src/index.ts
··· 4 4 import { getExtensions } from "@moonlight-mod/core/extension"; 5 5 import { loadExtensions } from "@moonlight-mod/core/extension/loader"; 6 6 import { MoonlightBranch, MoonlightNode } from "@moonlight-mod/types"; 7 + import { getConfig, getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config"; 7 8 import { IndexedDB } from "@zenfs/dom"; 8 - import { configure } from "@zenfs/core"; 9 + import { configureSingle } from "@zenfs/core"; 9 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"; 10 13 11 14 function getParts(path: string) { 12 15 if (path.startsWith("/")) path = path.substring(1); ··· 14 17 } 15 18 16 19 window._moonlightBrowserInit = async () => { 20 + delete window._moonlightBrowserInit; 21 + 17 22 // Set up a virtual filesystem with IndexedDB 18 - await configure({ 19 - mounts: { 20 - "/": { 21 - backend: IndexedDB, 22 - // eslint-disable-next-line @typescript-eslint/ban-ts-comment 23 - // @ts-ignore tsc tweaking 24 - storeName: "moonlight-fs" 25 - } 26 - } 23 + await configureSingle({ 24 + backend: IndexedDB, 25 + storeName: "moonlight-fs" 27 26 }); 28 27 29 - window.moonlightFS = { 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 - }, 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 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 - }, 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 58 59 - async rmdir(path) { 60 - const entries = await this.readdir(path); 59 + async rmdir(path) { 60 + const entries = await this.readdir(path); 61 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); 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 + } 69 70 } 70 - } 71 71 72 - await fs.rmdir(path); 73 - }, 72 + await fs.rmdir(path); 73 + }, 74 74 75 - async exists(path) { 76 - return await fs.exists(path); 77 - }, 78 - async isFile(path) { 79 - return (await fs.stat(path)).isFile(); 80 - }, 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 + }, 81 84 82 - join(...parts) { 83 - let str = parts.join("/"); 84 - if (!str.startsWith("/")) str = "/" + str; 85 - return str; 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 + } 86 98 }, 87 - dirname(path) { 88 - const parts = getParts(path); 89 - return "/" + parts.slice(0, parts.length - 1).join("/"); 90 - } 99 + // TODO 100 + addCors(url) {}, 101 + addBlocked(url) {} 91 102 }; 92 103 93 104 // Actual loading begins here 94 - const config = await readConfig(); 105 + let config = await readConfig(); 95 106 initLogger(config); 96 107 97 108 const extensions = await getExtensions(); 98 109 const processedExtensions = await loadExtensions(extensions); 99 110 100 - function getConfig(ext: string) { 101 - const val = config.extensions[ext]; 102 - if (val == null || typeof val === "boolean") return undefined; 103 - return val.config; 104 - } 105 - 106 111 const moonlightNode: MoonlightNode = { 107 - config, 112 + get config() { 113 + return config; 114 + }, 108 115 extensions, 109 116 processedExtensions, 110 117 nativesCache: {}, 111 118 isBrowser: true, 119 + events: createEventEmitter<NodeEventType, NodeEventPayloads>(), 112 120 113 121 version: MOONLIGHT_VERSION, 114 122 branch: MOONLIGHT_BRANCH as MoonlightBranch, 115 123 116 - getConfig, 117 - getConfigOption: <T>(ext: string, name: string) => { 118 - const config = getConfig(ext); 119 - if (config == null) return undefined; 120 - const option = config[name]; 121 - if (option == null) return undefined; 122 - return option as T; 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); 123 130 }, 131 + async setConfigOption(ext, name, value) { 132 + setConfigOption(config, ext, name, value); 133 + await this.writeConfig(config); 134 + }, 135 + 124 136 getNatives: () => {}, 125 137 getLogger: (id: string) => { 126 138 return new Logger(id); ··· 133 145 return `/extensions/${ext}`; 134 146 }, 135 147 136 - writeConfig 148 + async writeConfig(newConfig) { 149 + await writeConfig(newConfig); 150 + config = newConfig; 151 + this.events.dispatchEvent(NodeEventType.ConfigSaved, newConfig); 152 + } 137 153 }; 138 154 139 155 Object.assign(window, { ··· 141 157 }); 142 158 143 159 // This is set by web-preload for us 144 - await window._moonlightBrowserLoad(); 160 + await window._moonlightWebLoad!(); 145 161 };
+1
packages/browser/tsconfig.json
··· 1 1 { 2 2 "extends": "../../tsconfig.json", 3 3 "compilerOptions": { 4 + "lib": ["DOM", "ESNext", "ESNext.AsyncIterable"], 4 5 "module": "ES2022" 5 6 } 6 7 }
+7
packages/core/package.json
··· 4 4 "exports": { 5 5 "./*": "./src/*.ts" 6 6 }, 7 + "engineStrict": true, 8 + "engines": { 9 + "node": ">=22", 10 + "pnpm": ">=10", 11 + "npm": "pnpm", 12 + "yarn": "pnpm" 13 + }, 7 14 "dependencies": { 8 15 "@moonlight-mod/types": "workspace:*" 9 16 }
+4 -8
packages/core/src/config.ts
··· 6 6 const logger = new Logger("core/config"); 7 7 8 8 const defaultConfig: Config = { 9 + // If you're updating this, update `builtinExtensions` in constants as well 9 10 extensions: { 10 11 moonbase: true, 11 12 disableSentry: true, ··· 18 19 export async function writeConfig(config: Config) { 19 20 try { 20 21 const configPath = await getConfigPath(); 21 - await moonlightFS.writeFileString( 22 - configPath, 23 - JSON.stringify(config, null, 2) 24 - ); 22 + await moonlightNodeSandboxed.fs.writeFileString(configPath, JSON.stringify(config, null, 2)); 25 23 } catch (e) { 26 24 logger.error("Failed to write config", e); 27 25 } ··· 33 31 } 34 32 35 33 const configPath = await getConfigPath(); 36 - if (!(await moonlightFS.exists(configPath))) { 34 + if (!(await moonlightNodeSandboxed.fs.exists(configPath))) { 37 35 await writeConfig(defaultConfig); 38 36 return defaultConfig; 39 37 } else { 40 38 try { 41 - let config: Config = JSON.parse( 42 - await moonlightFS.readFileString(configPath) 43 - ); 39 + let config: Config = JSON.parse(await moonlightNodeSandboxed.fs.readFileString(configPath)); 44 40 // Assign the default values if they don't exist (newly added) 45 41 config = { ...defaultConfig, ...config }; 46 42 await writeConfig(config);
+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 + }
+62 -59
packages/core/src/extension/loader.ts
··· 13 13 import calculateDependencies from "../util/dependency"; 14 14 import { createEventEmitter } from "../util/event"; 15 15 import { registerStyles } from "../styles"; 16 - import { EventPayloads, EventType } from "@moonlight-mod/types/core/event"; 16 + import { WebEventPayloads, WebEventType } from "@moonlight-mod/types/core/event"; 17 17 18 18 const logger = new Logger("core/extension/loader"); 19 19 20 - function loadExtWeb(ext: DetectedExtension) { 20 + function evalIIFE(id: string, source: string): ExtensionWebExports { 21 + const fn = new Function("require", "module", "exports", source); 22 + 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) { 21 47 if (ext.scripts.web != null) { 22 - const source = ext.scripts.web; 23 - const fn = new Function("require", "module", "exports", source); 48 + const source = ext.scripts.web + `\n//# sourceURL=${ext.id}/web.js`; 24 49 25 - const module = { id: ext.id, exports: {} }; 26 - fn.apply(window, [ 27 - () => { 28 - logger.warn("Attempted to require() from web"); 29 - }, 30 - module, 31 - module.exports 32 - ]); 50 + let exports: ExtensionWebExports; 33 51 34 - const exports: ExtensionWebExports = module.exports; 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 + } 58 + 35 59 if (exports.patches != null) { 36 60 let idx = 0; 37 61 for (const patch of exports.patches) { 38 62 if (Array.isArray(patch.replace)) { 39 - for (const replacement of patch.replace) { 40 - const newPatch = Object.assign({}, patch, { 41 - replace: replacement 42 - }); 43 - 44 - registerPatch({ ...newPatch, ext: ext.id, id: idx }); 45 - idx++; 46 - } 63 + registerPatch({ ...patch, ext: ext.id, id: idx }); 47 64 } else { 48 - registerPatch({ ...patch, ext: ext.id, id: idx }); 49 - idx++; 65 + registerPatch({ ...patch, replace: [patch.replace], ext: ext.id, id: idx }); 50 66 } 67 + idx++; 51 68 } 52 69 } 53 70 54 71 if (exports.webpackModules != null) { 55 72 for (const [name, wp] of Object.entries(exports.webpackModules)) { 56 73 if (wp.run == null && ext.scripts.webpackModules?.[name] != null) { 57 - const func = new Function( 58 - "module", 59 - "exports", 60 - "require", 61 - ext.scripts.webpackModules[name]! 62 - ) as WebpackModuleFunc; 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; 63 76 registerWebpackModule({ 64 77 ...wp, 65 78 ext: ext.id, ··· 73 86 } 74 87 75 88 if (exports.styles != null) { 76 - registerStyles( 77 - exports.styles.map((style, i) => `/* ${ext.id}#${i} */ ${style}`) 78 - ); 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}`]); 79 93 } 80 94 } 81 95 } 82 96 83 97 async function loadExt(ext: DetectedExtension) { 84 98 webTarget: { 85 - loadExtWeb(ext); 99 + try { 100 + await loadExtWeb(ext); 101 + } catch (e) { 102 + logger.error(`Failed to load extension "${ext.id}"`, e); 103 + } 86 104 } 87 105 88 106 nodePreload: { ··· 113 131 InvalidEnvironment 114 132 } 115 133 116 - export function checkExtensionCompat( 117 - manifest: ExtensionManifest 118 - ): ExtensionCompat { 134 + export function checkExtensionCompat(manifest: ExtensionManifest): ExtensionCompat { 119 135 let environment; 120 136 webTarget: { 121 137 environment = ExtensionEnvironment.Web; ··· 124 140 environment = ExtensionEnvironment.Desktop; 125 141 } 126 142 127 - if (manifest.apiLevel !== constants.apiLevel) 128 - return ExtensionCompat.InvalidApiLevel; 129 - if ( 130 - (manifest.environment ?? "both") !== "both" && 131 - manifest.environment !== environment 132 - ) 143 + if (manifest.apiLevel !== constants.apiLevel) return ExtensionCompat.InvalidApiLevel; 144 + if ((manifest.environment ?? "both") !== "both" && manifest.environment !== environment) 133 145 return ExtensionCompat.InvalidEnvironment; 134 146 return ExtensionCompat.Compatible; 135 147 } ··· 148 160 extensions fires an event on completion, which allows us to await the loading 149 161 of another extension, resolving dependencies & load order effectively. 150 162 */ 151 - export async function loadExtensions( 152 - exts: DetectedExtension[] 153 - ): Promise<ProcessedExtensions> { 154 - exts = exts.filter( 155 - (ext) => checkExtensionCompat(ext.manifest) === ExtensionCompat.Compatible 156 - ); 163 + export async function loadExtensions(exts: DetectedExtension[]): Promise<ProcessedExtensions> { 164 + exts = exts.filter((ext) => checkExtensionCompat(ext.manifest) === ExtensionCompat.Compatible); 157 165 158 166 const config = await readConfig(); 159 167 const items = exts ··· 193 201 }; 194 202 } 195 203 196 - export async function loadProcessedExtensions({ 197 - extensions, 198 - dependencyGraph 199 - }: ProcessedExtensions) { 200 - const eventEmitter = createEventEmitter<EventType, EventPayloads>(); 204 + export async function loadProcessedExtensions({ extensions, dependencyGraph }: ProcessedExtensions) { 205 + const eventEmitter = createEventEmitter<WebEventType, WebEventPayloads>(); 201 206 const finished: Set<string> = new Set(); 202 207 203 208 logger.trace( ··· 219 224 } 220 225 221 226 function done() { 222 - eventEmitter.removeEventListener(EventType.ExtensionLoad, cb); 227 + eventEmitter.removeEventListener(WebEventType.ExtensionLoad, cb); 223 228 r(); 224 229 } 225 230 226 - eventEmitter.addEventListener(EventType.ExtensionLoad, cb); 231 + eventEmitter.addEventListener(WebEventType.ExtensionLoad, cb); 227 232 if (finished.has(dep)) done(); 228 233 }) 229 234 ); 230 235 231 236 if (waitPromises.length > 0) { 232 - logger.debug( 233 - `Waiting on ${waitPromises.length} dependencies for "${ext.id}"` 234 - ); 237 + logger.debug(`Waiting on ${waitPromises.length} dependencies for "${ext.id}"`); 235 238 await Promise.all(waitPromises); 236 239 } 237 240 ··· 239 242 await loadExt(ext); 240 243 241 244 finished.add(ext.id); 242 - eventEmitter.dispatchEvent(EventType.ExtensionLoad, ext.id); 245 + eventEmitter.dispatchEvent(WebEventType.ExtensionLoad, ext.id); 243 246 logger.debug(`Loaded "${ext.id}"`); 244 247 } 245 248
+55 -78
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 3 import { getCoreExtensionsPath, getExtensionsPath } from "./util/data"; 9 4 import Logger from "./util/logger"; ··· 13 8 async function findManifests(dir: string): Promise<string[]> { 14 9 const ret = []; 15 10 16 - if (await moonlightFS.exists(dir)) { 17 - for (const file of await moonlightFS.readdir(dir)) { 18 - const path = moonlightFS.join(dir, file); 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); 19 14 if (file === "manifest.json") { 20 15 ret.push(path); 21 16 } 22 17 23 - if (!(await moonlightFS.isFile(path))) { 18 + if (!(await moonlightNodeSandboxed.fs.isFile(path))) { 24 19 ret.push(...(await findManifests(path))); 25 20 } 26 21 } ··· 31 26 32 27 async function loadDetectedExtensions( 33 28 dir: string, 34 - type: ExtensionLoadSource 29 + type: ExtensionLoadSource, 30 + seen: Set<string> 35 31 ): Promise<DetectedExtension[]> { 36 32 const ret: DetectedExtension[] = []; 37 33 38 34 const manifests = await findManifests(dir); 39 35 for (const manifestPath of manifests) { 40 36 try { 41 - if (!(await moonlightFS.exists(manifestPath))) continue; 42 - const dir = moonlightFS.dirname(manifestPath); 37 + if (!(await moonlightNodeSandboxed.fs.exists(manifestPath))) continue; 38 + const dir = moonlightNodeSandboxed.fs.dirname(manifestPath); 43 39 44 - const manifest: ExtensionManifest = JSON.parse( 45 - await moonlightFS.readFileString(manifestPath) 46 - ); 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); 47 46 48 - const webPath = moonlightFS.join(dir, "index.js"); 49 - const nodePath = moonlightFS.join(dir, "node.js"); 50 - const hostPath = moonlightFS.join(dir, "host.js"); 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"); 51 50 52 51 // if none exist (empty manifest) don't give a shit 53 52 if ( 54 - !moonlightFS.exists(webPath) && 55 - !moonlightFS.exists(nodePath) && 56 - !moonlightFS.exists(hostPath) 53 + !moonlightNodeSandboxed.fs.exists(webPath) && 54 + !moonlightNodeSandboxed.fs.exists(nodePath) && 55 + !moonlightNodeSandboxed.fs.exists(hostPath) 57 56 ) { 58 57 continue; 59 58 } 60 59 61 - const web = (await moonlightFS.exists(webPath)) 62 - ? await moonlightFS.readFileString(webPath) 60 + const web = (await moonlightNodeSandboxed.fs.exists(webPath)) 61 + ? await moonlightNodeSandboxed.fs.readFileString(webPath) 63 62 : undefined; 64 63 65 64 let url: string | undefined = undefined; 66 - const urlPath = moonlightFS.join(dir, constants.repoUrlFile); 67 - if ( 68 - type === ExtensionLoadSource.Normal && 69 - (await moonlightFS.exists(urlPath)) 70 - ) { 71 - url = await moonlightFS.readFileString(urlPath); 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); 72 68 } 73 69 74 70 const wpModules: Record<string, string> = {}; 75 - const wpModulesPath = moonlightFS.join(dir, "webpackModules"); 76 - if (await moonlightFS.exists(wpModulesPath)) { 77 - const wpModulesFile = await moonlightFS.readdir(wpModulesPath); 71 + const wpModulesPath = moonlightNodeSandboxed.fs.join(dir, "webpackModules"); 72 + if (await moonlightNodeSandboxed.fs.exists(wpModulesPath)) { 73 + const wpModulesFile = await moonlightNodeSandboxed.fs.readdir(wpModulesPath); 78 74 79 75 for (const wpModuleFile of wpModulesFile) { 80 76 if (wpModuleFile.endsWith(".js")) { 81 - wpModules[wpModuleFile.replace(".js", "")] = 82 - await moonlightFS.readFileString( 83 - moonlightFS.join(wpModulesPath, wpModuleFile) 84 - ); 77 + wpModules[wpModuleFile.replace(".js", "")] = await moonlightNodeSandboxed.fs.readFileString( 78 + moonlightNodeSandboxed.fs.join(wpModulesPath, wpModuleFile) 79 + ); 85 80 } 86 81 } 87 82 } 83 + 84 + const stylePath = moonlightNodeSandboxed.fs.join(dir, "style.css"); 88 85 89 86 ret.push({ 90 87 id: manifest.id, ··· 97 94 web, 98 95 webPath: web != null ? webPath : undefined, 99 96 webpackModules: wpModules, 100 - nodePath: (await moonlightFS.exists(nodePath)) ? nodePath : undefined, 101 - hostPath: (await moonlightFS.exists(hostPath)) ? hostPath : undefined 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 102 } 103 103 }); 104 - } catch (e) { 105 - logger.error(e, "Failed to load extension"); 104 + } catch (err) { 105 + logger.error(`Failed to load extension from "${manifestPath}":`, err); 106 106 } 107 107 } 108 108 ··· 112 112 async function getExtensionsNative(): Promise<DetectedExtension[]> { 113 113 const config = await readConfig(); 114 114 const res = []; 115 + const seen = new Set<string>(); 115 116 116 - res.push( 117 - ...(await loadDetectedExtensions( 118 - getCoreExtensionsPath(), 119 - ExtensionLoadSource.Core 120 - )) 121 - ); 122 - 123 - res.push( 124 - ...(await loadDetectedExtensions( 125 - await getExtensionsPath(), 126 - ExtensionLoadSource.Normal 127 - )) 128 - ); 117 + res.push(...(await loadDetectedExtensions(getCoreExtensionsPath(), ExtensionLoadSource.Core, seen))); 129 118 130 119 for (const devSearchPath of config.devSearchPaths ?? []) { 131 - res.push( 132 - ...(await loadDetectedExtensions( 133 - devSearchPath, 134 - ExtensionLoadSource.Developer 135 - )) 136 - ); 120 + res.push(...(await loadDetectedExtensions(devSearchPath, ExtensionLoadSource.Developer, seen))); 137 121 } 138 122 123 + res.push(...(await loadDetectedExtensions(await getExtensionsPath(), ExtensionLoadSource.Normal, seen))); 124 + 139 125 return res; 140 126 } 141 127 142 128 async function getExtensionsBrowser(): Promise<DetectedExtension[]> { 143 129 const ret: DetectedExtension[] = []; 130 + const seen = new Set<string>(); 144 131 145 - const coreExtensionsFs: Record<string, string> = JSON.parse( 146 - // @ts-expect-error shut up 147 - _moonlight_coreExtensionsStr 148 - ); 149 - const coreExtensions = Array.from( 150 - new Set(Object.keys(coreExtensionsFs).map((x) => x.split("/")[0])) 151 - ); 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]))); 152 134 153 135 for (const ext of coreExtensions) { 154 136 if (!coreExtensionsFs[`${ext}/index.js`]) continue; ··· 159 141 const wpModulesPath = `${ext}/webpackModules`; 160 142 for (const wpModuleFile of Object.keys(coreExtensionsFs)) { 161 143 if (wpModuleFile.startsWith(wpModulesPath)) { 162 - wpModules[ 163 - wpModuleFile.replace(wpModulesPath + "/", "").replace(".js", "") 164 - ] = coreExtensionsFs[wpModuleFile]; 144 + wpModules[wpModuleFile.replace(wpModulesPath + "/", "").replace(".js", "")] = coreExtensionsFs[wpModuleFile]; 165 145 } 166 146 } 167 147 ··· 173 153 }, 174 154 scripts: { 175 155 web, 176 - webpackModules: wpModules 156 + webpackModules: wpModules, 157 + style: coreExtensionsFs[`${ext}/style.css`] 177 158 } 178 159 }); 160 + seen.add(manifest.id); 179 161 } 180 162 181 - if (await moonlightFS.exists("/extensions")) { 182 - ret.push( 183 - ...(await loadDetectedExtensions( 184 - "/extensions", 185 - ExtensionLoadSource.Normal 186 - )) 187 - ); 163 + if (await moonlightNodeSandboxed.fs.exists("/extensions")) { 164 + ret.push(...(await loadDetectedExtensions("/extensions", ExtensionLoadSource.Normal, seen))); 188 165 } 189 166 190 167 return ret;
+6
packages/core/src/fs.ts
··· 39 39 async isFile(path) { 40 40 return fs.statSync(path).isFile(); 41 41 }, 42 + async isDir(path) { 43 + return fs.statSync(path).isDirectory(); 44 + }, 42 45 43 46 join(...parts) { 44 47 return path.join(...parts); 45 48 }, 46 49 dirname(dir) { 47 50 return path.dirname(dir); 51 + }, 52 + basename(dir) { 53 + return path.basename(dir); 48 54 } 49 55 }; 50 56 }
+138 -122
packages/core/src/patch.ts
··· 11 11 } from "@moonlight-mod/types"; 12 12 import Logger from "./util/logger"; 13 13 import calculateDependencies, { Dependency } from "./util/dependency"; 14 - import WebpackRequire from "@moonlight-mod/types/discord/require"; 15 - import { EventType } from "@moonlight-mod/types/core/event"; 14 + import { WebEventType } from "@moonlight-mod/types/core/event"; 15 + import { processFind, processReplace, testFind } from "./util/patch"; 16 16 17 17 const logger = new Logger("core/patch"); 18 18 ··· 21 21 let webpackModules: Set<IdentifiedWebpackModule> = new Set(); 22 22 let webpackRequire: WebpackRequireType | null = null; 23 23 24 - const moduleLoadSubscriptions: Map<string, ((moduleId: string) => void)[]> = 25 - new Map(); 24 + const moduleLoadSubscriptions: Map<string, ((moduleId: string) => void)[]> = new Map(); 26 25 27 26 export function registerPatch(patch: IdentifiedPatch) { 27 + patch.find = processFind(patch.find); 28 + processReplace(patch.replace); 29 + 28 30 patches.push(patch); 29 31 moonlight.unpatched.add(patch); 30 32 } ··· 36 38 } 37 39 } 38 40 39 - export function onModuleLoad( 40 - module: string | string[], 41 - callback: (moduleId: string) => void 42 - ): void { 41 + export function onModuleLoad(module: string | string[], callback: (moduleId: string) => void): void { 43 42 let moduleIds = module; 44 43 45 44 if (typeof module === "string") { ··· 67 66 const moduleCache: Record<string, string> = {}; 68 67 const patched: Record<string, Array<string>> = {}; 69 68 70 - function patchModules(entry: WebpackJsonpEntry[1]) { 71 - function patchModule(id: string, patchId: string, replaced: string) { 72 - // Store what extensions patched what modules for easier debugging 73 - patched[id] = patched[id] || []; 74 - patched[id].push(patchId); 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); 75 83 76 - // Webpack module arguments are minified, so we replace them with consistent names 77 - // We have to wrap it so things don't break, though 78 - const patchedStr = patched[id].sort().join(", "); 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(", "); 79 87 80 - const wrapped = 81 - `(${replaced}).apply(this, arguments)\n` + 82 - `// Patched by moonlight: ${patchedStr}\n` + 83 - `//# sourceURL=Webpack-Module-${id}`; 88 + const wrapped = 89 + `(${replaced}).apply(this, arguments)\n` + `// Patched by moonlight: ${patchedStr}\n` + createSourceURL(id); 84 90 85 - try { 86 - const func = new Function( 87 - "module", 88 - "exports", 89 - "require", 90 - wrapped 91 - ) as WebpackModuleFunc; 92 - entry[id] = func; 93 - entry[id].__moonlight = true; 94 - return true; 95 - } catch (e) { 96 - logger.warn("Error constructing function for patch", patchId, e); 97 - patched[id].pop(); 98 - return false; 99 - } 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 100 } 101 + } 101 102 103 + function patchModules(entry: WebpackJsonpEntry[1]) { 102 104 // Populate the module cache 103 105 for (const [id, func] of Object.entries(entry)) { 104 106 if (!Object.hasOwn(moduleCache, id) && func.__moonlight !== true) { ··· 109 111 110 112 for (const [id, func] of Object.entries(entry)) { 111 113 if (func.__moonlight === true) continue; 112 - let moduleString = moduleCache[id]; 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>(); 113 124 114 125 for (let i = 0; i < patches.length; i++) { 115 126 const patch = patches[i]; 116 127 if (patch.prerequisite != null && !patch.prerequisite()) { 128 + moonlight.unpatched.delete(patch); 117 129 continue; 118 130 } 119 131 ··· 122 134 patch.find.lastIndex = 0; 123 135 } 124 136 125 - // indexOf is faster than includes by 0.25% lmao 126 - const match = 127 - typeof patch.find === "string" 128 - ? moduleString.indexOf(patch.find) !== -1 129 - : patch.find.test(moduleString); 137 + const match = testFind(origModuleString, patch.find) || patch.find === mappedName; 130 138 131 139 // Global regexes apply to all modules 132 - const shouldRemove = 133 - typeof patch.find === "string" ? true : !patch.find.global; 140 + const shouldRemove = typeof patch.find === "string" ? true : !patch.find.global; 134 141 142 + let replaced = moduleString; 143 + let hardFailed = false; 135 144 if (match) { 136 - moonlight.unpatched.delete(patch); 145 + // We ensured normal PatchReplace objects get turned into arrays on register 146 + const replaces = patch.replace as PatchReplace[]; 137 147 138 - // We ensured all arrays get turned into normal PatchReplace objects on register 139 - const replace = patch.replace as PatchReplace; 140 - 141 - if ( 142 - replace.type === undefined || 143 - replace.type === PatchReplaceType.Normal 144 - ) { 145 - // Add support for \i to match rspack's minified names 146 - if (typeof replace.match !== "string") { 147 - replace.match = new RegExp( 148 - replace.match.source.replace(/\\i/g, "[A-Za-z_$][\\w$]*"), 149 - replace.match.flags 150 - ); 151 - } 152 - // tsc fails to detect the overloads for this, so I'll just do this 153 - // Verbose, but it works 154 - let replaced; 155 - if (typeof replace.replacement === "string") { 156 - replaced = moduleString.replace(replace.match, replace.replacement); 157 - } else { 158 - replaced = moduleString.replace(replace.match, replace.replacement); 159 - } 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); 160 154 161 - if (replaced === moduleString) { 162 - logger.warn("Patch replacement failed", id, patch); 163 - continue; 164 - } 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 + } 165 163 166 - if (patchModule(id, `${patch.ext}#${patch.id}`, replaced)) { 167 - moduleString = replaced; 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; 168 181 } 169 - } else if (replace.type === PatchReplaceType.Module) { 170 - // Directly replace the module with a new one 171 - const newModule = replace.replacement(moduleString); 172 - entry[id] = newModule; 173 - entry[id].__moonlight = true; 174 - moduleString = 175 - newModule.toString().replace(/\n/g, "") + 176 - `//# sourceURL=Webpack-Module-${id}`; 177 182 } 178 183 179 - if (shouldRemove) { 180 - patches.splice(i--, 1); 184 + if (!hardFailed) { 185 + moduleString = replaced; 186 + modified = true; 187 + exts.add(patch.ext); 181 188 } 189 + 190 + if (isPatched) moonlight.unpatched.delete(patch); 191 + if (shouldRemove) patches.splice(i--, 1); 182 192 } 183 193 } 184 194 185 - moduleCache[id] = moduleString; 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 + } 186 201 187 202 try { 188 203 const parsed = moonlight.lunast.parseScript(id, moduleString); 189 204 if (parsed != null) { 190 205 for (const [parsedId, parsedScript] of Object.entries(parsed)) { 191 - if (patchModule(parsedId, "lunast", parsedScript)) { 206 + if (patchModule(parsedId, "lunast", parsedScript, entry)) { 192 207 moduleCache[parsedId] = parsedScript; 193 208 } 194 209 } ··· 198 213 } 199 214 200 215 if (moonlightNode.config.patchAll === true) { 201 - if ( 202 - (typeof id !== "string" || !id.includes("_")) && 203 - !entry[id].__moonlight 204 - ) { 205 - const wrapped = 206 - `(${moduleCache[id]}).apply(this, arguments)\n` + 207 - `//# sourceURL=Webpack-Module-${id}`; 208 - entry[id] = new Function( 209 - "module", 210 - "exports", 211 - "require", 212 - wrapped 213 - ) 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; 214 219 entry[id].__moonlight = true; 215 220 } 216 221 } ··· 246 251 function handleModuleDependencies() { 247 252 const modules = Array.from(webpackModules.values()); 248 253 249 - const dependencies: Dependency<string, IdentifiedWebpackModule>[] = 250 - modules.map((wp) => { 251 - return { 252 - id: depToString(wp), 253 - data: wp 254 - }; 255 - }); 254 + const dependencies: Dependency<string, IdentifiedWebpackModule>[] = modules.map((wp) => { 255 + return { 256 + id: depToString(wp), 257 + data: wp 258 + }; 259 + }); 256 260 257 261 const [sorted, _] = calculateDependencies(dependencies, { 258 262 fetchDep: (id) => { ··· 263 267 const deps = item.data?.dependencies ?? []; 264 268 return ( 265 269 deps.filter( 266 - (dep) => 267 - !(dep instanceof RegExp || typeof dep === "string") && 268 - dep.ext != null 270 + (dep) => !(dep instanceof RegExp || typeof dep === "string") && dep.ext != null 269 271 ) as ExplicitExtensionDependency[] 270 272 ).map(depToString); 271 273 } ··· 297 299 if (dep.test(modStr)) deps.delete(dep); 298 300 } else if ( 299 301 dep.ext != null 300 - ? injectedWpModules.find( 301 - (x) => x.ext === dep.ext && x.id === dep.id 302 - ) 302 + ? injectedWpModules.find((x) => x.ext === dep.ext && x.id === dep.id) 303 303 : injectedWpModules.find((x) => x.id === dep.id) 304 304 ) { 305 305 deps.delete(dep); 306 306 } 307 307 } 308 308 309 + wpModule.dependencies = Array.from(deps); 309 310 if (deps.size !== 0) { 310 - wpModule.dependencies = Array.from(deps); 311 311 continue; 312 312 } 313 - 314 - wpModule.dependencies = Array.from(deps); 315 313 } 316 314 } 317 315 ··· 324 322 if (wpModule.run) { 325 323 modules[id] = wpModule.run; 326 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); 327 334 } 328 - if (wpModule.entrypoint) entrypoints.push(id); 329 335 } 330 336 if (!webpackModules.size) break; 331 337 } 332 338 333 - for (const [name, func] of Object.entries( 334 - moonlight.moonmap.getWebpackModules("window.moonlight.moonmap") 335 - )) { 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; 336 342 injectedWpModules.push({ id: name, run: func }); 337 343 modules[name] = func; 338 344 inject = true; ··· 349 355 window.webpackChunkdiscord_app.push([ 350 356 [--chunkId], 351 357 modules, 352 - (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 + }) 353 370 ]); 354 371 } 355 372 } ··· 399 416 const realPush = jsonp.push; 400 417 if (jsonp.push.__moonlight !== true) { 401 418 jsonp.push = (items) => { 402 - moonlight.events.dispatchEvent(EventType.ChunkLoad, { 419 + moonlight.events.dispatchEvent(WebEventType.ChunkLoad, { 403 420 chunkId: items[0], 404 421 modules: items[1], 405 422 require: items[2] ··· 447 464 set(modules: any) { 448 465 const { stack } = new Error(); 449 466 if (stack!.includes("/assets/") && !Array.isArray(modules)) { 450 - moonlight.events.dispatchEvent(EventType.ChunkLoad, { 467 + moonlight.events.dispatchEvent(WebEventType.ChunkLoad, { 451 468 modules: modules 452 469 }); 453 470 patchModules(modules); 454 471 455 - if (!window.webpackChunkdiscord_app) 456 - window.webpackChunkdiscord_app = []; 472 + if (!window.webpackChunkdiscord_app) window.webpackChunkdiscord_app = []; 457 473 injectModules(modules); 458 474 } 459 475
+2 -12
packages/core/src/persist.ts
··· 1 1 import { join, dirname } from "node:path"; 2 - import { 3 - mkdirSync, 4 - renameSync, 5 - existsSync, 6 - copyFileSync, 7 - readdirSync 8 - } from "node:fs"; 2 + import { mkdirSync, renameSync, existsSync, copyFileSync, readdirSync } from "node:fs"; 9 3 import Logger from "./util/logger"; 10 4 11 5 const logger = new Logger("core/persist"); ··· 31 25 if (event === "host-updated") { 32 26 const versions = this.queryCurrentVersionsSync(); 33 27 34 - const newRootDir = join( 35 - this.rootPath, 36 - "app-" + 37 - versions.current_host.map((v: number) => v.toString()).join(".") 38 - ); 28 + const newRootDir = join(this.rootPath, "app-" + versions.current_host.map((v: number) => v.toString()).join(".")); 39 29 logger.info(`Persisting moonlight - new root dir: ${newRootDir}`); 40 30 41 31 const newResources = join(newRootDir, "resources");
+1 -4
packages/core/src/util/binary.ts
··· 55 55 return data; 56 56 } 57 57 58 - private _read<T>( 59 - func: (position: number, littleEndian?: boolean) => T, 60 - length: number 61 - ): T { 58 + private _read<T>(func: (position: number, littleEndian?: boolean) => T, length: number): T { 62 59 const result = func.call(this.view, this.position, true); 63 60 this.position += length; 64 61 return result;
+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 + }
+10 -15
packages/core/src/util/data.ts
··· 16 16 appData = electron.ipcRenderer.sendSync(constants.ipcGetAppData); 17 17 } 18 18 19 - const dir = moonlightFS.join(appData, "moonlight-mod"); 20 - if (!(await moonlightFS.exists(dir))) await moonlightFS.mkdir(dir); 19 + const dir = moonlightNodeSandboxed.fs.join(appData, "moonlight-mod"); 20 + if (!(await moonlightNodeSandboxed.fs.exists(dir))) await moonlightNodeSandboxed.fs.mkdir(dir); 21 21 22 22 return dir; 23 23 } ··· 36 36 37 37 let configPath = ""; 38 38 39 - const buildInfoPath = moonlightFS.join( 40 - process.resourcesPath, 41 - "build_info.json" 42 - ); 43 - if (!(await moonlightFS.exists(buildInfoPath))) { 44 - configPath = moonlightFS.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"); 45 42 } else { 46 - const buildInfo: BuildInfo = JSON.parse( 47 - await moonlightFS.readFileString(buildInfoPath) 48 - ); 49 - configPath = moonlightFS.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"); 50 45 } 51 46 52 47 return configPath; ··· 55 50 async function getPathFromMoonlight(...names: string[]) { 56 51 const dir = await getMoonlightDir(); 57 52 58 - const target = moonlightFS.join(dir, ...names); 59 - if (!(await moonlightFS.exists(target))) await moonlightFS.mkdir(target); 53 + const target = moonlightNodeSandboxed.fs.join(dir, ...names); 54 + if (!(await moonlightNodeSandboxed.fs.exists(target))) await moonlightNodeSandboxed.fs.mkdir(target); 60 55 61 56 return target; 62 57 } ··· 66 61 } 67 62 68 63 export function getCoreExtensionsPath(): string { 69 - return moonlightFS.join(__dirname, constants.coreExtensionsDir); 64 + return moonlightNodeSandboxed.fs.join(__dirname, constants.coreExtensionsDir); 70 65 }
+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);
+7 -27
packages/core/src/util/event.ts
··· 9 9 const listeners = new Map<(data: EventData) => void, (e: Event) => void>(); 10 10 11 11 return { 12 - dispatchEvent: <Id extends keyof EventData>( 13 - id: Id, 14 - data: EventData[Id] 15 - ) => { 16 - eventEmitter.dispatchEvent( 17 - new CustomEvent(id as string, { detail: data }) 18 - ); 12 + dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => { 13 + eventEmitter.dispatchEvent(new CustomEvent(id as string, { detail: data })); 19 14 }, 20 15 21 - addEventListener: <Id extends keyof EventData>( 22 - id: Id, 23 - cb: (data: EventData[Id]) => void 24 - ) => { 16 + addEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => { 25 17 const untyped = cb as (data: EventData) => void; 26 18 if (listeners.has(untyped)) return; 27 19 ··· 34 26 eventEmitter.addEventListener(id as string, listener); 35 27 }, 36 28 37 - removeEventListener: <Id extends keyof EventData>( 38 - id: Id, 39 - cb: (data: EventData[Id]) => void 40 - ) => { 29 + removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => { 41 30 const untyped = cb as (data: EventData) => void; 42 31 const listener = listeners.get(untyped); 43 32 if (listener == null) return; ··· 53 42 const listeners = new Map<(data: EventData) => void, (e: Event) => void>(); 54 43 55 44 return { 56 - dispatchEvent: <Id extends keyof EventData>( 57 - id: Id, 58 - data: EventData[Id] 59 - ) => { 45 + dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => { 60 46 eventEmitter.emit(id as string, data); 61 47 }, 62 48 63 - addEventListener: <Id extends keyof EventData>( 64 - id: Id, 65 - cb: (data: EventData[Id]) => void 66 - ) => { 49 + addEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => { 67 50 const untyped = cb as (data: EventData) => void; 68 51 if (listeners.has(untyped)) return; 69 52 ··· 76 59 eventEmitter.on(id as string, listener); 77 60 }, 78 61 79 - removeEventListener: <Id extends keyof EventData>( 80 - id: Id, 81 - cb: (data: EventData[Id]) => void 82 - ) => { 62 + removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => { 83 63 const untyped = cb as (data: EventData) => void; 84 64 const listener = listeners.get(untyped); 85 65 if (listener == null) return;
+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 }
+2 -8
packages/core/src/util/logger.ts
··· 50 50 if (maxLevel > level) return; 51 51 52 52 if (MOONLIGHT_WEB_PRELOAD || MOONLIGHT_BROWSER) { 53 - args = [ 54 - `%c[${logLevel}]`, 55 - `background-color: ${colors[level]}; color: #FFFFFF;`, 56 - `[${this.name}]`, 57 - ...obj 58 - ]; 53 + args = [`%c[${logLevel}]`, `background-color: ${colors[level]}; color: #FFFFFF;`, `[${this.name}]`, ...obj]; 59 54 } else { 60 55 args = [`[${logLevel}]`, `[${this.name}]`, ...obj]; 61 56 } ··· 87 82 88 83 export function initLogger(config: Config) { 89 84 if (config.loggerLevel != null) { 90 - const enumValue = 91 - LogLevel[config.loggerLevel.toUpperCase() as keyof typeof LogLevel]; 85 + const enumValue = LogLevel[config.loggerLevel.toUpperCase() as keyof typeof LogLevel]; 92 86 if (enumValue != null) { 93 87 maxLevel = enumValue; 94 88 }
+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
··· 1 1 { 2 - "extends": "../../tsconfig.json" 2 + "extends": "../../tsconfig.json", 3 + "compilerOptions": { 4 + "lib": ["ESNext", "DOM"] 5 + } 3 6 }
+9 -1
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 12 "@moonlight-mod/core": "workspace:*", 6 13 "@moonlight-mod/types": "workspace:*", 7 - "nanotar": "^0.1.1" 14 + "microdiff": "catalog:prod", 15 + "nanotar": "catalog:prod" 8 16 } 9 17 }
+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
··· 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
··· 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
··· 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
··· 1 + { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 3 + "id": "commands", 4 + "apiLevel": 2, 5 + "meta": { 6 + "name": "Commands", 7 + "tagline": "A library to add commands", 8 + "authors": ["Cynosphere", "NotNite"], 9 + "tags": ["library"] 10 + } 11 + }
+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;
+7 -5
packages/core-extensions/src/common/index.ts
··· 2 2 3 3 export const webpackModules: ExtensionWebExports["webpackModules"] = { 4 4 stores: { 5 - dependencies: [ 6 - { 7 - id: "discord/packages/flux" 8 - } 9 - ] 5 + dependencies: [{ id: "discord/packages/flux" }] 6 + }, 7 + ErrorBoundary: { 8 + dependencies: [{ id: "react" }] 9 + }, 10 + icons: { 11 + dependencies: [{ id: "react" }, { id: "discord/components/common/index" }] 10 12 } 11 13 };
+2 -1
packages/core-extensions/src/common/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "common", 3 4 "apiLevel": 2, 4 5 "meta": { 5 6 "name": "Common", 6 - "tagline": "A *lot* of common clientmodding utilities from the Discord client", 7 + "tagline": "Common client modding utilities for the Discord client", 7 8 "authors": ["Cynosphere", "NotNite"], 8 9 "tags": ["library"] 9 10 },
+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
··· 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;
+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;
+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
··· 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
··· 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
··· 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
··· 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 -14
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: 9 - /(?<=let{navId[^}]+?}=(.),(.)=function .\(.\){.+(?=,.=function))/, 10 - replacement: (_, props, items) => 11 - `,__contextMenu=!${props}.__contextMenu_evilMenu&&require("contextMenu_contextMenu")._patchMenu(${props}, ${items})` 8 + match: /(?<=let{navId[^}]+?}=(.),.=).+?(?=,)/, 9 + replacement: (items, props) => `require("contextMenu_contextMenu")._patchMenu(${props},${items})` 12 10 } 13 11 ] 14 12 }, ··· 17 15 replace: [ 18 16 { 19 17 match: /(?<=let\{[^}]+?\}=.;return ).\({[^}]+?}\)/, 20 - replacement: (render) => 21 - `require("contextMenu_contextMenu")._saveProps(this,${render})` 18 + replacement: (render) => `require("contextMenu_contextMenu")._saveProps(this,${render})` 22 19 } 23 20 ] 24 21 } ··· 26 23 27 24 export const webpackModules: Record<string, ExtensionWebpackModule> = { 28 25 contextMenu: { 29 - dependencies: [ 30 - { ext: "spacepack", id: "spacepack" }, 31 - "Menu API only allows Items and groups of Items as children." 32 - ] 26 + dependencies: [{ ext: "spacepack", id: "spacepack" }, "Menu API only allows Items and groups of Items as children."] 33 27 }, 34 28 evilMenu: { 35 - dependencies: [ 36 - { ext: "spacepack", id: "spacepack" }, 37 - "Menu API only allows Items and groups of Items as children." 38 - ] 29 + dependencies: [{ ext: "spacepack", id: "spacepack" }, "Menu API only allows Items and groups of Items as children."] 39 30 } 40 31 };
+1
packages/core-extensions/src/contextMenu/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "contextMenu", 3 4 "apiLevel": 2, 4 5 "meta": {
+32 -31
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 - export 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; 46 - export function _saveProps(self: any, el: any) { 41 + function _saveProps(self: any, el: any) { 47 42 menuProps = el.props; 48 43 49 44 const original = self.props.closeContextMenu; ··· 55 50 return el; 56 51 } 57 52 53 + module.exports = { 54 + patches, 55 + addItem, 56 + _patchMenu, 57 + _saveProps 58 + }; 59 + 58 60 // Unmangle Menu elements 61 + // spacepack.require.m[moonlight.moonmap.modules["discord/modules/menus/web/Menu"]].toString(); 59 62 const code = 60 63 spacepack.require.m[ 61 - spacepack.findByCode( 62 - "Menu API only allows Items and groups of Items as children." 63 - )[0].id 64 + spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id 64 65 ].toString(); 65 66 66 67 let MangledMenu;
+11 -21
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(/,.=(?=function .\(.\){.+?,.=function)/, ";return "); 10 - code = code.replace(/,(?=__contextMenu)/, ";let "); 11 - const mod = new Function( 12 - "module", 13 - "exports", 14 - "require", 15 - `(${code}).apply(this, arguments)` 16 - ); 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 + 17 14 const exp: any = {}; 18 15 mod({}, exp, require); 19 - const Menu = spacepack.findFunctionByStrings( 20 - exp, 21 - "Menu API only allows Items and groups of Items as children." 22 - )!; 23 - module.exports = (el: any) => { 24 - return Menu({ 25 - children: el, 26 - __contextMenu_evilMenu: true 27 - }); 28 - }; 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
··· 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
··· 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 -8
packages/core-extensions/src/disableSentry/host.ts
··· 5 5 6 6 if (moonlightHost.asarPath !== "moonlightDesktop") { 7 7 try { 8 - const hostSentryPath = require.resolve( 9 - join(moonlightHost.asarPath, "node_modules", "@sentry", "electron") 10 - ); 11 - require.cache[hostSentryPath] = new Module( 12 - hostSentryPath, 13 - require.cache[require.resolve(moonlightHost.asarPath)] 14 - ); 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)]); 15 10 require.cache[hostSentryPath]!.exports = { 16 11 init: () => {}, 17 12 captureException: () => {}, 18 13 setTag: () => {}, 19 - setUser: () => {} 14 + setUser: () => {}, 15 + captureMessage: () => {} 20 16 }; 21 17 logger.debug("Stubbed Sentry host side!"); 22 18 } catch (err) {
+2 -2
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 {
+5 -1
packages/core-extensions/src/disableSentry/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "disableSentry", 3 4 "apiLevel": 2, 4 5 "meta": { ··· 11 12 "https://*.sentry.io/*", 12 13 "https://*.discord.com/error-reporting-proxy/*", 13 14 "https://discord.com/assets/sentry.*.js", 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" 15 19 ] 16 20 }
+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
··· 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
··· 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 ];
+11 -2
packages/core-extensions/src/experiments/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "experiments", 3 4 "apiLevel": 2, 4 5 "meta": { ··· 8 9 "tags": ["dangerZone"] 9 10 }, 10 11 "settings": { 11 - "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", 12 20 "displayName": "Allow access to other staff settings elsewhere", 13 - "type": "boolean" 21 + "type": "boolean", 22 + "default": false 14 23 } 15 24 } 16 25 }
+6 -18
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 12 match: /(?<=;(.{1,2}\.Z)={RULES:.+?})/, 14 - replacement: (_, rulesets) => 15 - `;require("markdown_markdown")._applyRulesetBlacklist(${rulesets});` 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"subtext":{(.+?)}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 ];
+1
packages/core-extensions/src/markdown/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "markdown", 3 4 "apiLevel": 2, 4 5 "meta": {
+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
··· 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 + });
+59 -59
packages/core-extensions/src/moonbase/index.tsx
··· 1 - import { ExtensionWebpackModule } 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 34 export const webpackModules: Record<string, ExtensionWebpackModule> = { 4 35 stores: { 5 - dependencies: [ 6 - { id: "discord/packages/flux" }, 7 - { id: "discord/Dispatcher" } 8 - ] 36 + dependencies: [{ id: "discord/packages/flux" }, { id: "discord/Dispatcher" }] 9 37 }, 10 38 11 39 ui: { ··· 14 42 { id: "react" }, 15 43 { id: "discord/components/common/index" }, 16 44 { ext: "moonbase", id: "stores" }, 17 - { id: "discord/modules/guild_settings/IntegrationCard.css" }, 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" }, 18 49 "Masks.PANEL_BUTTON", 19 50 '"Missing channel in Channel.openChannelContextMenu"', 20 51 ".forumOrHome]:" 21 52 ] 22 53 }, 23 54 55 + ThemeDarkIcon: { 56 + dependencies: [{ ext: "common", id: "icons" }, { id: "react" }] 57 + }, 58 + 24 59 settings: { 25 60 dependencies: [ 26 61 { ext: "spacepack", id: "spacepack" }, 27 62 { ext: "settings", id: "settings" }, 28 63 { id: "react" }, 29 - { ext: "moonbase", id: "ui" } 64 + { ext: "moonbase", id: "ui" }, 65 + { ext: "contextMenu", id: "contextMenu" }, 66 + ':"USER_SETTINGS_MODAL_SET_SECTION"' 30 67 ], 31 68 entrypoint: true 32 69 }, ··· 35 72 dependencies: [ 36 73 { id: "react" }, 37 74 { ext: "moonbase", id: "stores" }, 75 + { ext: "moonbase", id: "ThemeDarkIcon" }, 38 76 { ext: "notices", id: "notices" }, 39 77 { 40 78 ext: "spacepack", 41 79 id: "spacepack" 42 - } 80 + }, 81 + { id: "discord/Constants" }, 82 + { id: "discord/components/common/index" } 43 83 ], 44 84 entrypoint: true 45 85 }, 46 86 47 87 moonbase: { 48 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 + ] 49 100 } 50 101 }; 51 - 52 - const bg = "#222034"; 53 - const fg = "#FFFBA6"; 54 - 55 - export const styles = [ 56 - ` 57 - .moonbase-settings > :first-child { 58 - margin-top: 0px; 59 - } 60 - 61 - textarea.moonbase-resizeable { 62 - resize: vertical 63 - } 64 - 65 - .moonbase-updates-notice { 66 - background-color: ${bg}; 67 - color: ${fg}; 68 - line-height: unset; 69 - height: 36px; 70 - } 71 - 72 - .moonbase-updates-notice button { 73 - color: ${fg}; 74 - border-color: ${fg}; 75 - } 76 - 77 - .moonbase-updates-notice_text-wrapper { 78 - display: inline-flex; 79 - align-items: center; 80 - line-height: 36px; 81 - gap: 2px; 82 - } 83 - 84 - .moonbase-update-section { 85 - background-color: ${bg}; 86 - --info-help-foreground: ${fg}; 87 - border: none !important; 88 - color: ${fg}; 89 - 90 - display: flex; 91 - flex-direction: row; 92 - justify-content: space-between; 93 - } 94 - 95 - .moonbase-update-section > button { 96 - color: ${fg}; 97 - background-color: transparent; 98 - border-color: ${fg}; 99 - } 100 - `.trim() 101 - ];
+18 -5
packages/core-extensions/src/moonbase/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "moonbase", 3 4 "apiLevel": 2, 4 5 "meta": { ··· 6 7 "tagline": "The official settings UI for moonlight", 7 8 "authors": ["Cynosphere", "NotNite", "redstonekasi"] 8 9 }, 9 - "dependencies": ["spacepack", "settings", "common", "notices"], 10 + "dependencies": ["spacepack", "settings", "common", "notices", "contextMenu"], 10 11 "settings": { 11 12 "sections": { 13 + "advice": "reload", 12 14 "displayName": "Split into sections", 13 15 "description": "Show the Moonbase tabs as separate sections", 14 - "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 15 24 }, 16 25 "saveFilter": { 26 + "advice": "none", 17 27 "displayName": "Persist filter", 18 28 "description": "Save extension filter in config", 19 - "type": "boolean" 29 + "type": "boolean", 30 + "default": false 20 31 }, 21 32 "updateChecking": { 33 + "advice": "none", 22 34 "displayName": "Automatic update checking", 23 35 "description": "Checks for updates to moonlight", 24 36 "type": "boolean", 25 - "default": "true" 37 + "default": true 26 38 }, 27 39 "updateBanner": { 40 + "advice": "none", 28 41 "displayName": "Show update banner", 29 42 "description": "Shows a banner for moonlight and extension updates", 30 43 "type": "boolean", 31 - "default": "true" 44 + "default": true 32 45 } 33 46 }, 34 47 "cors": [
+46 -71
packages/core-extensions/src/moonbase/native.ts
··· 1 1 import { MoonlightBranch } from "@moonlight-mod/types"; 2 2 import type { MoonbaseNatives, RepositoryManifest } from "./types"; 3 3 import extractAsar from "@moonlight-mod/core/asar"; 4 - import { 5 - distDir, 6 - repoUrlFile, 7 - installedVersionFile 8 - } from "@moonlight-mod/types/constants"; 4 + import { distDir, repoUrlFile, installedVersionFile } from "@moonlight-mod/types/constants"; 9 5 import { parseTarGzip } from "nanotar"; 6 + 7 + const moonlightGlobal = globalThis.moonlightHost ?? globalThis.moonlightNode; 10 8 11 9 const githubRepo = "moonlight-mod/moonlight"; 12 10 const githubApiUrl = `https://api.github.com/repos/${githubRepo}/releases/latest`; ··· 15 13 const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref"; 16 14 const nightlyZipUrl = "https://moonlight-mod.github.io/moonlight/dist.tar.gz"; 17 15 18 - export const userAgent = `moonlight/${moonlightNode.version} (https://github.com/moonlight-mod/moonlight)`; 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; 19 22 20 23 async function getStableRelease(): Promise<{ 21 24 name: string; ··· 26 29 }> { 27 30 const req = await fetch(githubApiUrl, { 28 31 cache: "no-store", 29 - headers: { 30 - "User-Agent": userAgent 31 - } 32 + headers: sharedHeaders 32 33 }); 33 34 return await req.json(); 34 35 } 35 36 36 37 export default function getNatives(): MoonbaseNatives { 37 - const logger = moonlightNode.getLogger("moonbase/natives"); 38 + const logger = moonlightGlobal.getLogger("moonbase/natives"); 38 39 39 40 return { 40 41 async checkForMoonlightUpdate() { 41 42 try { 42 - if (moonlightNode.branch === MoonlightBranch.STABLE) { 43 + if (moonlightGlobal.branch === MoonlightBranch.STABLE) { 43 44 const json = await getStableRelease(); 44 - return json.name !== moonlightNode.version ? json.name : null; 45 - } else if (moonlightNode.branch === MoonlightBranch.NIGHTLY) { 45 + return json.name !== moonlightGlobal.version ? json.name : null; 46 + } else if (moonlightGlobal.branch === MoonlightBranch.NIGHTLY) { 46 47 const req = await fetch(nightlyRefUrl, { 47 48 cache: "no-store", 48 - headers: { 49 - "User-Agent": userAgent 50 - } 49 + headers: sharedHeaders 51 50 }); 52 51 const ref = (await req.text()).split("\n")[0]; 53 - return ref !== moonlightNode.version ? ref : null; 52 + return ref !== moonlightGlobal.version ? ref : null; 54 53 } 55 54 56 55 return null; ··· 60 59 } 61 60 }, 62 61 63 - async updateMoonlight() { 62 + async updateMoonlight(overrideBranch?: MoonlightBranch) { 63 + const branch = overrideBranch ?? moonlightGlobal.branch; 64 + 64 65 // Note: this won't do anything on browser, we should probably disable it 65 66 // entirely when running in browser. 66 67 async function downloadStable(): Promise<[ArrayBuffer, string]> { ··· 71 72 logger.debug(`Downloading ${asset.browser_download_url}`); 72 73 const req = await fetch(asset.browser_download_url, { 73 74 cache: "no-store", 74 - headers: { 75 - "User-Agent": userAgent 76 - } 75 + headers: sharedHeaders 77 76 }); 78 77 79 78 return [await req.arrayBuffer(), json.name]; ··· 83 82 logger.debug(`Downloading ${nightlyZipUrl}`); 84 83 const zipReq = await fetch(nightlyZipUrl, { 85 84 cache: "no-store", 86 - headers: { 87 - "User-Agent": userAgent 88 - } 85 + headers: sharedHeaders 89 86 }); 90 87 91 88 const refReq = await fetch(nightlyRefUrl, { 92 89 cache: "no-store", 93 - headers: { 94 - "User-Agent": userAgent 95 - } 90 + headers: sharedHeaders 96 91 }); 97 92 const ref = (await refReq.text()).split("\n")[0]; 98 93 ··· 100 95 } 101 96 102 97 const [tar, ref] = 103 - moonlightNode.branch === MoonlightBranch.STABLE 98 + branch === MoonlightBranch.STABLE 104 99 ? await downloadStable() 105 - : moonlightNode.branch === MoonlightBranch.NIGHTLY 100 + : branch === MoonlightBranch.NIGHTLY 106 101 ? await downloadNightly() 107 102 : [null, null]; 108 103 109 104 if (!tar || !ref) return; 110 105 111 - const dist = moonlightFS.join(moonlightNode.getMoonlightDir(), distDir); 112 - if (await moonlightFS.exists(dist)) await moonlightFS.rmdir(dist); 113 - await moonlightFS.mkdir(dist); 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); 114 109 115 110 logger.debug("Extracting update"); 116 111 const files = await parseTarGzip(tar); ··· 119 114 // @ts-expect-error What do you mean their own types are wrong 120 115 if (file.type !== "file") continue; 121 116 122 - const fullFile = moonlightFS.join(dist, file.name); 123 - const fullDir = moonlightFS.dirname(fullFile); 124 - if (!(await moonlightFS.exists(fullDir))) 125 - await moonlightFS.mkdir(fullDir); 126 - await moonlightFS.writeFile(fullFile, file.data); 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); 127 121 } 128 122 129 123 logger.debug("Writing version file:", ref); 130 - const versionFile = moonlightFS.join( 131 - moonlightNode.getMoonlightDir(), 132 - installedVersionFile 133 - ); 134 - await moonlightFS.writeFileString(versionFile, ref.trim()); 124 + const versionFile = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), installedVersionFile); 125 + await moonlightNodeSandboxed.fs.writeFileString(versionFile, ref.trim()); 135 126 136 127 logger.debug("Update extracted"); 137 128 }, ··· 143 134 try { 144 135 const req = await fetch(repo, { 145 136 cache: "no-store", 146 - headers: { 147 - "User-Agent": userAgent 148 - } 137 + headers: sharedHeaders 149 138 }); 150 139 const json = await req.json(); 151 140 ret[repo] = json; ··· 159 148 160 149 async installExtension(manifest, url, repo) { 161 150 const req = await fetch(url, { 162 - headers: { 163 - "User-Agent": userAgent 164 - } 151 + cache: "no-store", 152 + headers: sharedHeaders 165 153 }); 166 154 167 - const dir = moonlightNode.getExtensionDir(manifest.id); 155 + const dir = moonlightGlobal.getExtensionDir(manifest.id); 168 156 // remake it in case of updates 169 - if (await moonlightFS.exists(dir)) await moonlightFS.rmdir(dir); 170 - await moonlightFS.mkdir(dir); 157 + if (await moonlightNodeSandboxed.fs.exists(dir)) await moonlightNodeSandboxed.fs.rmdir(dir); 158 + await moonlightNodeSandboxed.fs.mkdir(dir); 171 159 172 160 const buffer = await req.arrayBuffer(); 173 161 const files = extractAsar(buffer); 174 162 for (const [file, buf] of Object.entries(files)) { 175 - const fullFile = moonlightFS.join(dir, file); 176 - const fullDir = moonlightFS.dirname(fullFile); 163 + const fullFile = moonlightNodeSandboxed.fs.join(dir, file); 164 + const fullDir = moonlightNodeSandboxed.fs.dirname(fullFile); 177 165 178 - if (!(await moonlightFS.exists(fullDir))) 179 - await moonlightFS.mkdir(fullDir); 180 - await moonlightFS.writeFile(moonlightFS.join(dir, file), buf); 166 + if (!(await moonlightNodeSandboxed.fs.exists(fullDir))) await moonlightNodeSandboxed.fs.mkdir(fullDir); 167 + await moonlightNodeSandboxed.fs.writeFile(moonlightNodeSandboxed.fs.join(dir, file), buf); 181 168 } 182 169 183 - await moonlightFS.writeFileString( 184 - moonlightFS.join(dir, repoUrlFile), 185 - repo 186 - ); 170 + await moonlightNodeSandboxed.fs.writeFileString(moonlightNodeSandboxed.fs.join(dir, repoUrlFile), repo); 187 171 }, 188 172 189 173 async deleteExtension(id) { 190 - const dir = moonlightNode.getExtensionDir(id); 191 - await moonlightFS.rmdir(dir); 192 - }, 193 - 194 - getExtensionConfig(id, key) { 195 - const config = moonlightNode.config.extensions[id]; 196 - if (typeof config === "object") { 197 - return config.config?.[key]; 198 - } 199 - 200 - return undefined; 174 + const dir = moonlightGlobal.getExtensionDir(id); 175 + await moonlightNodeSandboxed.fs.rmdir(dir); 201 176 } 202 177 }; 203 178 }
+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 + }
+21 -11
packages/core-extensions/src/moonbase/types.ts
··· 1 1 import { ExtensionCompat } from "@moonlight-mod/core/extension/loader"; 2 - import { DetectedExtension, ExtensionManifest } from "@moonlight-mod/types"; 2 + import { DetectedExtension, ExtensionManifest, MoonlightBranch } from "@moonlight-mod/types"; 3 3 4 4 export type MoonbaseNatives = { 5 5 checkForMoonlightUpdate(): Promise<string | null>; 6 - updateMoonlight(): Promise<void>; 6 + updateMoonlight(overrideBranch?: MoonlightBranch): Promise<void>; 7 7 8 - fetchRepositories( 9 - repos: string[] 10 - ): Promise<Record<string, RepositoryManifest[]>>; 11 - installExtension( 12 - manifest: RepositoryManifest, 13 - url: string, 14 - repo: string 15 - ): Promise<void>; 8 + fetchRepositories(repos: string[]): Promise<Record<string, RepositoryManifest[]>>; 9 + installExtension(manifest: RepositoryManifest, url: string, repo: string): Promise<void>; 16 10 deleteExtension(id: string): Promise<void>; 17 - getExtensionConfig(id: string, key: string): any; 18 11 }; 19 12 20 13 export type RepositoryManifest = ExtensionManifest & { ··· 35 28 state: ExtensionState; 36 29 compat: ExtensionCompat; 37 30 hasUpdate: boolean; 31 + changelog?: string; 32 + settingsOverride?: ExtensionManifest["settings"]; 38 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
··· 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
··· 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 + }
+27 -40
packages/core-extensions/src/moonbase/webpackModules/settings.tsx
··· 1 1 import settings from "@moonlight-mod/wp/settings_settings"; 2 2 import React from "@moonlight-mod/wp/react"; 3 3 import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; 4 - import { Moonbase, pages } from "@moonlight-mod/wp/moonbase_ui"; 5 - 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"; 6 7 import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores"; 7 - import * as Components from "@moonlight-mod/wp/discord/components/common/index"; 8 - 9 - import Update from "./ui/update"; 10 - 11 - const { MenuItem, Text, Breadcrumbs } = Components; 12 - 13 - const Margins = spacepack.require("discord/styles/shared/Margins.css"); 14 - 15 - const { open } = spacepack.findByExports("setSection", "clearSubsection")[0] 16 - .exports.Z; 8 + import { Text, Breadcrumbs } from "@moonlight-mod/wp/discord/components/common/index"; 9 + import { MenuItem } from "@moonlight-mod/wp/contextMenu_contextMenu"; 17 10 18 11 const notice = { 19 12 stores: [MoonbaseSettingsStore], 20 13 element: () => { 21 14 // Require it here because lazy loading SUX 22 - const SettingsNotice = spacepack.findByCode( 23 - "onSaveButtonColor", 24 - "FocusRingScope" 25 - )[0].exports.Z; 15 + const SettingsNotice = spacepack.require("discord/components/common/SettingsNotice").default; 26 16 return ( 27 17 <SettingsNotice 28 18 submitting={MoonbaseSettingsStore.submitting} 29 19 onReset={() => { 30 20 MoonbaseSettingsStore.reset(); 31 21 }} 32 - onSave={() => { 33 - MoonbaseSettingsStore.writeConfig(); 22 + onSave={async () => { 23 + await MoonbaseSettingsStore.writeConfig(); 34 24 }} 35 25 /> 36 26 ); 37 27 } 38 28 }; 39 29 40 - function addSection( 41 - id: string, 42 - name: string, 43 - element: React.FunctionComponent 44 - ) { 45 - settings.addSection(`moonbase-${id}`, name, element, null, -2, notice); 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); 46 35 } 47 36 48 37 // FIXME: move to component types ··· 53 42 54 43 function renderBreadcrumb(crumb: Breadcrumb, last: boolean) { 55 44 return ( 56 - <Text 57 - variant="heading-lg/semibold" 58 - tag="h2" 59 - color={last ? "header-primary" : "header-secondary"} 60 - > 45 + <Text variant="heading-lg/semibold" tag="h2" color={last ? "header-primary" : "header-secondary"}> 61 46 {crumb.label} 62 47 </Text> 63 48 ); 64 49 } 65 50 66 - if ( 67 - MoonbaseSettingsStore.getExtensionConfigRaw<boolean>( 68 - "moonbase", 69 - "sections", 70 - false 71 - ) 72 - ) { 73 - settings.addHeader("Moonbase", -2); 51 + if (!oldLocation) { 52 + settings.addDivider(position); 53 + } 54 + 55 + if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) { 56 + if (oldLocation) settings.addHeader("Moonbase", position); 74 57 75 - for (const page of pages) { 58 + const _pages = oldLocation ? pages : pages.reverse(); 59 + for (const page of _pages) { 76 60 addSection(page.id, page.name, () => { 77 61 const breadcrumbs = [ 78 62 { id: "moonbase", label: "Moonbase" }, ··· 89 73 {page.name} 90 74 </Breadcrumbs> 91 75 76 + <RestartAdviceMessage /> 92 77 <Update /> 93 78 94 79 <page.element /> ··· 96 81 ); 97 82 }); 98 83 } 84 + 85 + if (!oldLocation) settings.addHeader("Moonbase", position); 99 86 } else { 100 - settings.addSection("moonbase", "Moonbase", Moonbase, null, -2, notice); 87 + settings.addSection("moonbase", "Moonbase", Moonbase, null, position, notice); 101 88 102 89 settings.addSectionMenuItems( 103 90 "moonbase", ··· 106 93 key={page.id} 107 94 id={`moonbase-${page.id}`} 108 95 label={page.name} 109 - action={() => open("moonbase", i)} 96 + action={() => UserSettingsModalActionCreators.open("moonbase", i.toString())} 110 97 /> 111 98 )) 112 99 );
+270 -143
packages/core-extensions/src/moonbase/webpackModules/stores.ts
··· 1 - import { Config, ExtensionLoadSource } from "@moonlight-mod/types"; 1 + import { Config, ExtensionEnvironment, ExtensionLoadSource, ExtensionSettingsAdvice } from "@moonlight-mod/types"; 2 2 import { 3 3 ExtensionState, 4 4 MoonbaseExtension, 5 5 MoonbaseNatives, 6 - RepositoryManifest 6 + RepositoryManifest, 7 + RestartAdvice, 8 + UpdateState 7 9 } from "../types"; 8 10 import { Store } from "@moonlight-mod/wp/discord/packages/flux"; 9 11 import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher"; 10 12 import getNatives from "../native"; 11 13 import { mainRepo } from "@moonlight-mod/types/constants"; 12 - import { 13 - checkExtensionCompat, 14 - ExtensionCompat 15 - } from "@moonlight-mod/core/extension/loader"; 14 + import { checkExtensionCompat, ExtensionCompat } from "@moonlight-mod/core/extension/loader"; 16 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"; 17 19 18 20 const logger = moonlight.getLogger("moonbase"); 19 21 ··· 21 23 if (moonlightNode.isBrowser) natives = getNatives(); 22 24 23 25 class MoonbaseSettingsStore extends Store<any> { 24 - private origConfig: Config; 26 + private initialConfig: Config; 27 + private savedConfig: Config; 25 28 private config: Config; 26 29 private extensionIndex: number; 27 - private configComponents: Record<string, Record<string, CustomComponent>> = 28 - {}; 30 + private configComponents: Record<string, Record<string, CustomComponent>> = {}; 29 31 30 32 modified: boolean; 31 33 submitting: boolean; 32 34 installing: boolean; 33 35 36 + #updateState = UpdateState.Ready; 37 + get updateState() { 38 + return this.#updateState; 39 + } 34 40 newVersion: string | null; 35 41 shouldShowNotice: boolean; 42 + 43 + restartAdvice = RestartAdvice.NotNeeded; 36 44 37 45 extensions: { [id: number]: MoonbaseExtension }; 38 46 updates: { ··· 46 54 constructor() { 47 55 super(Dispatcher); 48 56 49 - this.origConfig = moonlightNode.config; 50 - this.config = this.clone(this.origConfig); 57 + this.initialConfig = moonlightNode.config; 58 + this.savedConfig = moonlightNode.config; 59 + this.config = this.clone(this.savedConfig); 51 60 this.extensionIndex = 0; 52 61 53 62 this.modified = false; ··· 64 73 this.extensions[uniqueId] = { 65 74 ...ext, 66 75 uniqueId, 67 - state: moonlight.enabledExtensions.has(ext.id) 68 - ? ExtensionState.Enabled 69 - : ExtensionState.Disabled, 76 + state: moonlight.enabledExtensions.has(ext.id) ? ExtensionState.Enabled : ExtensionState.Disabled, 70 77 compat: checkExtensionCompat(ext.manifest), 71 78 hasUpdate: false 72 79 }; 73 80 } 74 81 75 - natives! 76 - .fetchRepositories(this.config.repositories) 77 - .then((ret) => { 78 - for (const [repo, exts] of Object.entries(ret)) { 79 - try { 80 - for (const ext of exts) { 81 - const uniqueId = this.extensionIndex++; 82 - const extensionData = { 83 - id: ext.id, 84 - uniqueId, 85 - manifest: ext, 86 - source: { type: ExtensionLoadSource.Normal, url: repo }, 87 - state: ExtensionState.NotDownloaded, 88 - compat: ExtensionCompat.Compatible, 89 - hasUpdate: false 90 - }; 82 + // This is async but we're calling it without 83 + this.checkUpdates(); 91 84 92 - // Don't present incompatible updates 93 - if (checkExtensionCompat(ext) !== ExtensionCompat.Compatible) 94 - continue; 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 + } 95 94 96 - const existing = this.getExisting(extensionData); 97 - if (existing != null) { 98 - // Make sure the download URL is properly updated 99 - for (const [id, e] of Object.entries(this.extensions)) { 100 - if (e.id === ext.id && e.source.url === repo) { 101 - this.extensions[parseInt(id)].manifest = { 102 - ...e.manifest, 103 - download: ext.download 104 - }; 105 - break; 106 - } 107 - } 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 + }; 108 124 109 - if (this.hasUpdate(extensionData)) { 110 - this.updates[existing.uniqueId] = { 111 - version: ext.version!, 112 - download: ext.download, 113 - updateManifest: ext 114 - }; 115 - existing.hasUpdate = true; 116 - } 125 + // Don't present incompatible updates 126 + if (checkExtensionCompat(ext) !== ExtensionCompat.Compatible) continue; 117 127 118 - continue; 119 - } 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 + }; 120 135 121 - this.extensions[uniqueId] = extensionData; 122 - } 123 - } catch (e) { 124 - logger.error(`Error processing repository ${repo}`, e); 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; 125 144 } 145 + } else { 146 + this.extensions[uniqueId] = extensionData; 126 147 } 148 + } 149 + } 150 + } 127 151 128 - this.emitChange(); 129 - }) 130 - .then(() => 131 - this.getExtensionConfigRaw("moonbase", "updateChecking", true) 132 - ? natives!.checkForMoonlightUpdate() 133 - : new Promise<null>((resolve) => resolve(null)) 134 - ) 135 - .then((version) => { 136 - this.newVersion = version; 137 - this.emitChange(); 138 - }) 139 - .then(() => { 140 - this.shouldShowNotice = 141 - this.newVersion != null || Object.keys(this.updates).length > 0; 142 - this.emitChange(); 143 - }); 152 + private async checkMoonlightUpdates() { 153 + this.newVersion = this.getExtensionConfigRaw("moonbase", "updateChecking", true) 154 + ? await natives!.checkForMoonlightUpdate() 155 + : null; 144 156 } 145 157 146 158 private getExisting(ext: MoonbaseExtension) { 147 - return Object.values(this.extensions).find( 148 - (e) => e.id === ext.id && e.source.url === ext.source.url 149 - ); 159 + return Object.values(this.extensions).find((e) => e.id === ext.id && e.source.url === ext.source.url); 150 160 } 151 161 152 162 private hasUpdate(ext: MoonbaseExtension) { 153 - const existing = Object.values(this.extensions).find( 154 - (e) => e.id === ext.id && e.source.url === ext.source.url 155 - ); 163 + const existing = Object.values(this.extensions).find((e) => e.id === ext.id && e.source.url === ext.source.url); 156 164 if (existing == null) return false; 157 165 158 - return ( 159 - existing.manifest.version !== ext.manifest.version && 160 - existing.state !== ExtensionState.NotDownloaded 161 - ); 166 + return existing.manifest.version !== ext.manifest.version && existing.state !== ExtensionState.NotDownloaded; 162 167 } 163 168 164 169 // Jank 165 170 private isModified() { 166 - const orig = JSON.stringify(this.origConfig); 171 + const orig = JSON.stringify(this.savedConfig); 167 172 const curr = JSON.stringify(this.config); 168 173 return orig !== curr; 169 174 } ··· 172 177 return this.submitting || this.installing; 173 178 } 174 179 180 + // Required for the settings store contract 175 181 showNotice() { 176 182 return this.modified; 177 183 } ··· 181 187 } 182 188 183 189 getExtensionUniqueId(id: string) { 184 - return Object.values(this.extensions).find((ext) => ext.id === id) 185 - ?.uniqueId; 190 + return Object.values(this.extensions).find((ext) => ext.id === id)?.uniqueId; 186 191 } 187 192 188 193 getExtensionConflicting(uniqueId: number) { 189 194 const ext = this.getExtension(uniqueId); 190 195 if (ext.state !== ExtensionState.NotDownloaded) return false; 191 196 return Object.values(this.extensions).some( 192 - (e) => 193 - e.id === ext.id && 194 - e.uniqueId !== uniqueId && 195 - e.state !== ExtensionState.NotDownloaded 197 + (e) => e.id === ext.id && e.uniqueId !== uniqueId && e.state !== ExtensionState.NotDownloaded 196 198 ); 197 199 } 198 200 ··· 215 217 216 218 getExtensionConfig<T>(uniqueId: number, key: string): T | undefined { 217 219 const ext = this.getExtension(uniqueId); 218 - const defaultValue = ext.manifest.settings?.[key]?.default; 219 - const clonedDefaultValue = this.clone(defaultValue); 220 - const cfg = this.config.extensions[ext.id]; 221 - 222 - if (cfg == null || typeof cfg === "boolean") return clonedDefaultValue; 223 - return cfg.config?.[key] ?? clonedDefaultValue; 220 + const settings = ext.settingsOverride ?? ext.manifest.settings; 221 + return getConfigOption(ext.id, key, this.config, settings); 224 222 } 225 223 226 - getExtensionConfigRaw<T>( 227 - id: string, 228 - key: string, 229 - defaultValue: T | undefined 230 - ): T | undefined { 224 + getExtensionConfigRaw<T>(id: string, key: string, defaultValue: T | undefined): T | undefined { 231 225 const cfg = this.config.extensions[id]; 232 - 233 226 if (cfg == null || typeof cfg === "boolean") return defaultValue; 234 227 return cfg.config?.[key] ?? defaultValue; 235 228 } 236 229 237 230 getExtensionConfigName(uniqueId: number, key: string) { 238 231 const ext = this.getExtension(uniqueId); 239 - return ext.manifest.settings?.[key]?.displayName ?? key; 232 + const settings = ext.settingsOverride ?? ext.manifest.settings; 233 + return settings?.[key]?.displayName ?? key; 240 234 } 241 235 242 236 getExtensionConfigDescription(uniqueId: number, key: string) { 243 237 const ext = this.getExtension(uniqueId); 244 - return ext.manifest.settings?.[key]?.description; 238 + const settings = ext.settingsOverride ?? ext.manifest.settings; 239 + return settings?.[key]?.description; 245 240 } 246 241 247 242 setExtensionConfig(id: string, key: string, value: any) { 248 - const oldConfig = this.config.extensions[id]; 249 - const newConfig = 250 - typeof oldConfig === "boolean" 251 - ? { 252 - enabled: oldConfig, 253 - config: { [key]: value } 254 - } 255 - : { 256 - ...oldConfig, 257 - config: { ...(oldConfig?.config ?? {}), [key]: value } 258 - }; 259 - 260 - this.config.extensions[id] = newConfig; 243 + setConfigOption(this.config, id, key, value); 261 244 this.modified = this.isModified(); 262 245 this.emitChange(); 263 246 } ··· 267 250 let val = this.config.extensions[ext.id]; 268 251 269 252 if (val == null) { 270 - this.config.extensions[ext.id] = { enabled }; 253 + this.config.extensions[ext.id] = enabled; 271 254 this.modified = this.isModified(); 272 255 this.emitChange(); 273 256 return; ··· 284 267 this.emitChange(); 285 268 } 286 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 + 287 287 async installExtension(uniqueId: number) { 288 288 const ext = this.getExtension(uniqueId); 289 289 if (!("download" in ext.manifest)) { ··· 299 299 this.extensions[uniqueId].state = ExtensionState.Disabled; 300 300 } 301 301 302 - if (update != null) 303 - this.extensions[uniqueId].compat = checkExtensionCompat( 304 - update.updateManifest 305 - ); 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 + } 306 309 307 310 delete this.updates[uniqueId]; 308 311 } catch (e) { ··· 310 313 } 311 314 312 315 this.installing = false; 316 + this.restartAdvice = this.#computeRestartAdvice(); 313 317 this.emitChange(); 314 318 } 315 319 ··· 335 339 336 340 const deps: Record<string, MoonbaseExtension[]> = {}; 337 341 for (const dep of missingDeps) { 338 - const candidates = Object.values(this.extensions).filter( 339 - (e) => e.id === dep 340 - ); 342 + const candidates = Object.values(this.extensions).filter((e) => e.id === dep); 341 343 342 344 deps[dep] = candidates.sort((a, b) => { 343 345 const aRank = this.getRank(a); 344 346 const bRank = this.getRank(b); 345 347 if (aRank === bRank) { 346 - const repoIndex = this.config.repositories.indexOf(a.source.url!); 347 - const otherRepoIndex = this.config.repositories.indexOf( 348 - b.source.url! 349 - ); 348 + const repoIndex = this.savedConfig.repositories.indexOf(a.source.url!); 349 + const otherRepoIndex = this.savedConfig.repositories.indexOf(b.source.url!); 350 350 return repoIndex - otherRepoIndex; 351 351 } else { 352 352 return bRank - aRank; ··· 370 370 } 371 371 372 372 this.installing = false; 373 + this.restartAdvice = this.#computeRestartAdvice(); 373 374 this.emitChange(); 374 375 } 375 376 376 377 async updateMoonlight() { 377 - await natives.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 + 389 + this.emitChange(); 378 390 } 379 391 380 392 getConfigOption<K extends keyof Config>(key: K): Config[K] { ··· 392 404 return (uniqueId != null ? this.getExtensionName(uniqueId) : null) ?? id; 393 405 } 394 406 395 - registerConfigComponent( 396 - ext: string, 397 - name: string, 398 - component: CustomComponent 399 - ) { 407 + registerConfigComponent(ext: string, name: string, component: CustomComponent) { 400 408 if (!(ext in this.configComponents)) this.configComponents[ext] = {}; 401 409 this.configComponents[ext][name] = component; 402 410 } ··· 405 413 return this.configComponents[ext]?.[name]; 406 414 } 407 415 408 - writeConfig() { 409 - this.submitting = true; 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); 410 431 411 - moonlightNode.writeConfig(this.config); 412 - this.origConfig = this.clone(this.config); 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); 413 438 414 - this.submitting = false; 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() { 514 + try { 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(); 523 + } 524 + } 525 + 526 + private async processConfigChanged() { 527 + this.savedConfig = this.clone(this.config); 528 + this.restartAdvice = this.#computeRestartAdvice(); 415 529 this.modified = false; 530 + 531 + const modifiedRepos = diff(this.savedConfig.repositories, this.config.repositories); 532 + if (modifiedRepos.length !== 0) await this.checkUpdates(); 533 + 416 534 this.emitChange(); 417 535 } 418 536 419 537 reset() { 420 538 this.submitting = false; 421 539 this.modified = false; 422 - this.config = this.clone(this.origConfig); 540 + this.config = this.clone(this.savedConfig); 423 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 + } 424 551 } 425 552 426 553 // Required because electron likes to make it immutable sometimes.
+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
··· 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
··· 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 + }
+18 -43
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 5 import React from "@moonlight-mod/wp/react"; 8 6 import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; ··· 18 16 Clickable 19 17 } from "@moonlight-mod/wp/discord/components/common/index"; 20 18 import Flex from "@moonlight-mod/wp/discord/uikit/Flex"; 21 - import * as Components from "@moonlight-mod/wp/discord/components/common/index"; 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 - // FIXME: type component keys 42 - const { CircleXIcon } = Components; 43 - 44 39 function RemoveEntryButton({ onClick }: { onClick: () => void }) { 45 40 return ( 46 - <div className={RemoveButtonClasses.removeButtonContainer}> 41 + <div className={GuildSettingsRoleEditClasses.removeButtonContainer}> 47 42 <Tooltip text="Remove entry" position="top"> 48 43 {(props: any) => ( 49 - <Clickable 50 - {...props} 51 - className={RemoveButtonClasses.removeButton} 52 - onClick={onClick} 53 - > 44 + <Clickable {...props} className={GuildSettingsRoleEditClasses.removeButton} onClick={onClick}> 54 45 <CircleXIcon width={24} height={24} /> 55 46 </Clickable> 56 47 )} ··· 59 50 ); 60 51 } 61 52 62 - function ArrayFormItem({ 63 - config 64 - }: { 65 - config: "repositories" | "devSearchPaths"; 66 - }) { 53 + function ArrayFormItem({ config }: { config: "repositories" | "devSearchPaths" }) { 67 54 const items = MoonbaseSettingsStore.getConfigOption(config) ?? []; 68 55 return ( 69 56 <Flex ··· 123 110 <> 124 111 <FormSwitch 125 112 className={Margins.marginTop20} 126 - value={MoonbaseSettingsStore.getExtensionConfigRaw<boolean>( 127 - "moonbase", 128 - "updateChecking", 129 - true 130 - )} 113 + value={MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "updateChecking", true) ?? true} 131 114 onChange={(value: boolean) => { 132 - MoonbaseSettingsStore.setExtensionConfig( 133 - "moonbase", 134 - "updateChecking", 135 - value 136 - ); 115 + MoonbaseSettingsStore.setExtensionConfig("moonbase", "updateChecking", value); 137 116 }} 138 117 note="Checks for updates to moonlight" 139 118 > 140 119 Automatic update checking 141 120 </FormSwitch> 142 121 <FormItem title="Repositories"> 143 - <FormText className={Margins.marginBottom4}> 144 - A list of remote repositories to display extensions from 145 - </FormText> 122 + <FormText className={Margins.marginBottom4}>A list of remote repositories to display extensions from</FormText> 146 123 <ArrayFormItem config="repositories" /> 147 124 </FormItem> 148 - <FormDivider className={FormClasses.dividerDefault} /> 125 + <FormDivider className={FormSwitchClasses.dividerDefault} /> 149 126 <FormItem title="Extension search paths" className={Margins.marginTop20}> 150 127 <FormText className={Margins.marginBottom4}> 151 128 A list of local directories to search for built extensions 152 129 </FormText> 153 130 <ArrayFormItem config="devSearchPaths" /> 154 131 </FormItem> 155 - <FormDivider className={FormClasses.dividerDefault} /> 132 + <FormDivider className={FormSwitchClasses.dividerDefault} /> 156 133 <FormSwitch 157 134 className={Margins.marginTop20} 158 - value={MoonbaseSettingsStore.getConfigOption("patchAll")} 135 + value={MoonbaseSettingsStore.getConfigOption("patchAll") ?? false} 159 136 onChange={(value: boolean) => { 160 137 MoonbaseSettingsStore.setConfigOption("patchAll", value); 161 138 }} ··· 172 149 value: o.toLowerCase(), 173 150 label: o[0] + o.slice(1).toLowerCase() 174 151 }))} 175 - onChange={(v) => 176 - MoonbaseSettingsStore.setConfigOption("loggerLevel", v) 177 - } 152 + onChange={(v) => MoonbaseSettingsStore.setConfigOption("loggerLevel", v)} 178 153 /> 179 154 </FormItem> 180 155 </>
+253 -194
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
··· 1 1 import { ExtensionState } from "../../../types"; 2 - import { ExtensionLoadSource } from "@moonlight-mod/types"; 2 + import { constants, ExtensionLoadSource, ExtensionTag } from "@moonlight-mod/types"; 3 + 3 4 import { ExtensionCompat } from "@moonlight-mod/core/extension/loader"; 4 - 5 - import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; 6 - import * as Components from "@moonlight-mod/wp/discord/components/common/index"; 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"; 7 21 import React from "@moonlight-mod/wp/react"; 8 22 import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux"; 9 23 import Flex from "@moonlight-mod/wp/discord/uikit/Flex"; 10 24 import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils"; 11 - import IntegrationCard from "@moonlight-mod/wp/discord/modules/guild_settings/IntegrationCard.css"; 12 - 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"; 13 32 import ExtensionInfo from "./info"; 14 33 import Settings from "./settings"; 15 - import installWithDependencyPopup from "./popup"; 34 + import { doGenericExtensionPopup, doMissingExtensionPopup } from "./popup"; 16 35 17 36 export enum ExtensionPage { 18 37 Info, 19 38 Description, 39 + Changelog, 20 40 Settings 21 41 } 22 42 23 - import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores"; 24 - 25 - const { BeakerIcon, DownloadIcon, TrashIcon, CircleWarningIcon, Tooltip } = 26 - Components; 27 - 28 - const PanelButton = spacepack.findByCode("Masks.PANEL_BUTTON")[0].exports.Z; 29 - const TabBarClasses = spacepack.findByExports( 30 - "tabBar", 31 - "tabBarItem", 32 - "headerContentWrapper" 33 - )[0].exports; 34 - const MarkupClasses = spacepack.findByExports("markup", "inlineFormat")[0] 35 - .exports; 36 - 37 - const BuildOverrideClasses = spacepack.findByExports( 38 - "disabledButtonOverride" 39 - )[0].exports; 40 - 41 43 const COMPAT_TEXT_MAP: Record<ExtensionCompat, string> = { 42 44 [ExtensionCompat.Compatible]: "huh?", 43 45 [ExtensionCompat.InvalidApiLevel]: "Incompatible API level", 44 46 [ExtensionCompat.InvalidEnvironment]: "Incompatible platform" 45 47 }; 46 - 47 - export default function ExtensionCard({ uniqueId }: { uniqueId: number }) { 48 - const [tab, setTab] = React.useState(ExtensionPage.Info); 49 - const [restartNeeded, setRestartNeeded] = React.useState(false); 48 + const CONFLICTING_TEXT = "This extension is already installed from another source."; 50 49 51 - const { ext, enabled, busy, update, conflicting } = useStateFromStores( 52 - [MoonbaseSettingsStore], 53 - () => { 54 - return { 55 - ext: MoonbaseSettingsStore.getExtension(uniqueId), 56 - enabled: MoonbaseSettingsStore.getExtensionEnabled(uniqueId), 57 - busy: MoonbaseSettingsStore.busy, 58 - update: MoonbaseSettingsStore.getExtensionUpdate(uniqueId), 59 - conflicting: MoonbaseSettingsStore.getExtensionConflicting(uniqueId) 60 - }; 61 - } 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 + /> 62 59 ); 60 + } 63 61 64 - // Why it work like that :sob: 65 - if (ext == null) return <></>; 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 + }); 66 72 67 - const { Card, Text, FormSwitch, TabBar, Button } = Components; 73 + const [tab, setTab] = React.useState( 74 + update != null && ext?.changelog != null ? ExtensionPage.Changelog : ExtensionPage.Info 75 + ); 68 76 69 77 const tagline = ext.manifest?.meta?.tagline; 70 - const settings = ext.manifest?.settings; 78 + const settings = ext.settingsOverride ?? ext.manifest?.settings; 71 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); 90 + 72 91 const enabledDependants = useStateFromStores([MoonbaseSettingsStore], () => 73 92 Object.keys(MoonbaseSettingsStore.extensions) 74 93 .filter((uniqueId) => { 75 - const potentialDependant = MoonbaseSettingsStore.getExtension( 76 - parseInt(uniqueId) 77 - ); 94 + const potentialDependant = MoonbaseSettingsStore.getExtension(parseInt(uniqueId)); 78 95 79 96 return ( 80 - potentialDependant.manifest.dependencies?.includes(ext.id) && 97 + potentialDependant.manifest.dependencies?.includes(ext?.id) && 81 98 MoonbaseSettingsStore.getExtensionEnabled(parseInt(uniqueId)) 82 99 ); 83 100 }) ··· 85 102 ); 86 103 const implicitlyEnabled = enabledDependants.length > 0; 87 104 88 - return ( 89 - <Card editable={true} className={IntegrationCard.card}> 90 - <div className={IntegrationCard.cardHeader}> 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 + ); 111 + 112 + return ext == null ? ( 113 + <></> 114 + ) : ( 115 + <Card editable={true} className={AppCardClasses.card}> 116 + <div className={AppCardClasses.cardHeader}> 91 117 <Flex direction={Flex.Direction.VERTICAL}> 92 118 <Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER}> 93 - <Text variant="text-md/semibold"> 94 - {ext.manifest?.meta?.name ?? ext.id} 95 - </Text> 119 + <Text variant="text-md/semibold">{ext.manifest?.meta?.name ?? ext.id}</Text> 96 120 {ext.source.type === ExtensionLoadSource.Developer && ( 97 121 <Tooltip text="This is a local extension" position="top"> 98 - {(props: any) => ( 99 - <BeakerIcon 100 - {...props} 101 - class={BuildOverrideClasses.infoIcon} 102 - size="xs" 103 - /> 104 - )} 122 + {(props: any) => <ScienceIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />} 123 + </Tooltip> 124 + )} 125 + 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 + )} 131 + 132 + {ext.manifest?.meta?.deprecated && ( 133 + <Tooltip text="This extension is deprecated" position="top"> 134 + {(props: any) => <WarningIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />} 105 135 </Tooltip> 106 136 )} 107 137 </Flex> 108 138 109 - {tagline != null && ( 110 - <Text variant="text-sm/normal">{MarkupUtils.parse(tagline)}</Text> 111 - )} 139 + {tagline != null && <Text variant="text-sm/normal">{MarkupUtils.parse(tagline)}</Text>} 112 140 </Flex> 113 141 114 - <Flex 115 - direction={Flex.Direction.HORIZONTAL} 116 - align={Flex.Align.END} 117 - justify={Flex.Justify.END} 118 - > 119 - {ext.state === ExtensionState.NotDownloaded ? ( 120 - <Tooltip 121 - text={COMPAT_TEXT_MAP[ext.compat]} 122 - shouldShow={ext.compat !== ExtensionCompat.Compatible} 123 - > 124 - {(props: any) => ( 125 - <Button 126 - {...props} 127 - color={Button.Colors.BRAND} 128 - submitting={busy} 129 - disabled={ 130 - ext.compat !== ExtensionCompat.Compatible || conflicting 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 131 220 } 132 - onClick={async () => { 133 - await installWithDependencyPopup(uniqueId); 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 + } 134 243 }} 135 - > 136 - Install 137 - </Button> 138 - )} 139 - </Tooltip> 140 - ) : ( 141 - <div 142 - // too lazy to learn how <Flex /> works lmao 244 + /> 245 + </> 246 + )} 247 + </div> 248 + </Flex> 249 + </div> 250 + 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} 143 259 style={{ 144 - display: "flex", 145 - alignItems: "center", 146 - gap: "1rem" 260 + padding: "0 20px" 147 261 }} 148 262 > 149 - {ext.source.type === ExtensionLoadSource.Normal && ( 150 - <PanelButton 151 - icon={TrashIcon} 152 - tooltipText="Delete" 153 - onClick={() => { 154 - MoonbaseSettingsStore.deleteExtension(uniqueId); 155 - }} 156 - /> 263 + <TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Info}> 264 + Info 265 + </TabBar.Item> 266 + 267 + {description != null && ( 268 + <TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Description}> 269 + Description 270 + </TabBar.Item> 157 271 )} 158 272 159 - {update != null && ( 160 - <PanelButton 161 - icon={DownloadIcon} 162 - tooltipText="Update" 163 - onClick={() => { 164 - MoonbaseSettingsStore.installExtension(uniqueId); 165 - }} 166 - /> 273 + {changelog != null && ( 274 + <TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Changelog}> 275 + Changelog 276 + </TabBar.Item> 167 277 )} 168 278 169 - {restartNeeded && ( 170 - <PanelButton 171 - icon={() => ( 172 - <CircleWarningIcon 173 - color={Components.tokens.colors.STATUS_DANGER} 174 - /> 175 - )} 176 - onClick={() => window.location.reload()} 177 - tooltipText="You will need to reload/restart your client for this extension to work properly." 178 - /> 279 + {settings != null && ( 280 + <TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Settings}> 281 + Settings 282 + </TabBar.Item> 179 283 )} 180 - 181 - <FormSwitch 182 - value={ 183 - ext.compat === ExtensionCompat.Compatible && 184 - (enabled || implicitlyEnabled) 185 - } 186 - disabled={ 187 - implicitlyEnabled || ext.compat !== ExtensionCompat.Compatible 188 - } 189 - hideBorder={true} 190 - style={{ marginBottom: "0px" }} 191 - tooltipNote={ 192 - ext.compat !== ExtensionCompat.Compatible 193 - ? COMPAT_TEXT_MAP[ext.compat] 194 - : implicitlyEnabled 195 - ? `This extension is a dependency of the following enabled extension${ 196 - enabledDependants.length > 1 ? "s" : "" 197 - }: ${enabledDependants 198 - .map((a) => a.manifest.meta?.name ?? a.id) 199 - .join(", ")}` 200 - : undefined 201 - } 202 - onChange={() => { 203 - setRestartNeeded(true); 204 - MoonbaseSettingsStore.setExtensionEnabled(uniqueId, !enabled); 205 - }} 206 - /> 207 - </div> 208 - )} 209 - </Flex> 210 - </div> 284 + </TabBar> 211 285 212 - <div> 213 - {(description != null || settings != null) && ( 214 - <TabBar 215 - selectedItem={tab} 216 - type="top" 217 - onItemSelect={setTab} 218 - className={TabBarClasses.tabBar} 219 - style={{ 220 - padding: "0 20px" 221 - }} 222 - > 223 - <TabBar.Item 224 - className={TabBarClasses.tabBarItem} 225 - 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" 226 292 > 227 - Info 228 - </TabBar.Item> 229 - 230 - {description != null && ( 231 - <TabBar.Item 232 - className={TabBarClasses.tabBarItem} 233 - id={ExtensionPage.Description} 234 - > 235 - Description 236 - </TabBar.Item> 237 - )} 238 - 239 - {settings != null && ( 240 - <TabBar.Item 241 - className={TabBarClasses.tabBarItem} 242 - id={ExtensionPage.Settings} 243 - > 244 - Settings 245 - </TabBar.Item> 246 - )} 247 - </TabBar> 293 + {linkButtons.length > 0 && linkButtons} 294 + </Flex> 295 + </Flex> 248 296 )} 249 297 250 298 <Flex 251 299 justify={Flex.Justify.START} 252 300 wrap={Flex.Wrap.WRAP} 253 301 style={{ 254 - padding: "16px 16px" 302 + padding: "16px 16px", 303 + // This looks wonky in the settings tab 304 + rowGap: tab === ExtensionPage.Info ? "16px" : undefined 255 305 }} 256 306 > 257 - {tab === ExtensionPage.Info && <ExtensionInfo ext={ext} />} 307 + {tab === ExtensionPage.Info && <ExtensionInfo ext={ext} selectTag={selectTag} />} 258 308 {tab === ExtensionPage.Description && ( 259 - <Text 260 - variant="text-md/normal" 261 - class={MarkupClasses.markup} 262 - style={{ width: "100%" }} 263 - > 309 + <Text variant="text-md/normal" className={MarkupClasses.markup} style={{ width: "100%" }}> 264 310 {MarkupUtils.parse(description ?? "*No description*", true, { 265 311 allowHeading: true, 266 312 allowLinks: true, ··· 268 314 })} 269 315 </Text> 270 316 )} 271 - {tab === ExtensionPage.Settings && <Settings ext={ext} />} 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 + })} 324 + </Text> 325 + )} 326 + {tab === ExtensionPage.Settings && ( 327 + <ErrorBoundary> 328 + <Settings ext={ext} /> 329 + </ErrorBoundary> 330 + )} 272 331 </Flex> 273 332 </div> 274 333 </Card>
+109 -117
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 3 import * as React from "@moonlight-mod/wp/react"; 5 4 import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux"; ··· 11 10 Popout, 12 11 Dialog, 13 12 Menu, 14 - MenuGroup, 15 - MenuCheckboxItem, 16 - MenuItem 13 + ChevronSmallDownIcon, 14 + ChevronSmallUpIcon, 15 + ArrowsUpDownIcon, 16 + RetryIcon, 17 + Tooltip 17 18 } from "@moonlight-mod/wp/discord/components/common/index"; 18 - import * as Components 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, ··· 25 29 Disabled = 1 << 4, 26 30 Installed = 1 << 5, 27 31 Repository = 1 << 6, 28 - Incompatible = 1 << 7 32 + Incompatible = 1 << 7, 33 + Deprecated = 1 << 8 29 34 } 30 35 export const defaultFilter = 127 as Filter; 31 36 32 - const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports; 33 - const SortMenuClasses = spacepack.findByCode("container:", "clearText:")[0] 34 - .exports; 35 - 36 - let FilterDialogClasses: any; 37 - let FilterBarClasses: any; 37 + let HeaderClasses: any; 38 + let ForumsClasses: any; 39 + let SortMenuClasses: any; 38 40 spacepack 39 - .lazyLoad( 40 - '"Missing channel in Channel.openChannelContextMenu"', 41 - /e\("(\d+)"\)/g, 42 - /webpackId:(\d+?),/ 43 - ) 41 + .lazyLoad('"Missing channel in Channel.openChannelContextMenu"', /e\("(\d+)"\)/g, /webpackId:(\d+?),/) 44 42 .then(() => { 45 - FilterBarClasses = spacepack.findByCode("tagsButtonWithCount:")[0].exports; 46 - FilterDialogClasses = spacepack.findByCode( 47 - "countContainer:", 48 - "tagContainer:" 49 - )[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"); 50 46 }); 51 47 52 - const TagItem = spacepack.findByCode(".FORUM_TAG_A11Y_FILTER_BY_TAG")[0].exports 53 - .Z; 54 - 55 - // FIXME: type component keys 56 - const { ChevronSmallDownIcon, ChevronSmallUpIcon, ArrowsUpDownIcon } = 57 - Components; 58 - 59 - function toggleTag( 60 - selectedTags: Set<string>, 61 - setSelectedTags: (tags: Set<string>) => void, 62 - tag: string 63 - ) { 48 + function toggleTag(selectedTags: Set<string>, setSelectedTags: (tags: Set<string>) => void, tag: string) { 64 49 const newState = new Set(selectedTags); 65 50 if (newState.has(tag)) newState.delete(tag); 66 51 else newState.add(tag); ··· 76 61 setFilter: (filter: Filter) => void; 77 62 closePopout: () => void; 78 63 }) { 79 - const toggleFilter = (set: Filter) => 80 - setFilter(filter & set ? filter & ~set : filter | set); 64 + const toggleFilter = (set: Filter) => setFilter(filter & set ? filter & ~set : filter | set); 81 65 82 66 return ( 83 67 <div className={SortMenuClasses.container}> 84 - <Menu navId="sort-filter" hideScrollbar={true} onClose={closePopout}> 68 + <Menu navId="sort-filter" hideScroller={true} onClose={closePopout}> 85 69 <MenuGroup label="Type"> 86 70 <MenuCheckboxItem 87 71 id="t-core" 88 72 label="Core" 89 - checked={filter & Filter.Core} 73 + checked={(filter & Filter.Core) === Filter.Core} 90 74 action={() => toggleFilter(Filter.Core)} 91 75 /> 92 76 <MenuCheckboxItem 93 77 id="t-normal" 94 78 label="Normal" 95 - checked={filter & Filter.Normal} 79 + checked={(filter & Filter.Normal) === Filter.Normal} 96 80 action={() => toggleFilter(Filter.Normal)} 97 81 /> 98 82 <MenuCheckboxItem 99 83 id="t-developer" 100 84 label="Developer" 101 - checked={filter & Filter.Developer} 85 + checked={(filter & Filter.Developer) === Filter.Developer} 102 86 action={() => toggleFilter(Filter.Developer)} 103 87 /> 104 88 </MenuGroup> ··· 106 90 <MenuCheckboxItem 107 91 id="s-enabled" 108 92 label="Enabled" 109 - checked={filter & Filter.Enabled} 93 + checked={(filter & Filter.Enabled) === Filter.Enabled} 110 94 action={() => toggleFilter(Filter.Enabled)} 111 95 /> 112 96 <MenuCheckboxItem 113 97 id="s-disabled" 114 98 label="Disabled" 115 - checked={filter & Filter.Disabled} 99 + checked={(filter & Filter.Disabled) === Filter.Disabled} 116 100 action={() => toggleFilter(Filter.Disabled)} 117 101 /> 118 102 </MenuGroup> ··· 120 104 <MenuCheckboxItem 121 105 id="l-installed" 122 106 label="Installed" 123 - checked={filter & Filter.Installed} 107 + checked={(filter & Filter.Installed) === Filter.Installed} 124 108 action={() => toggleFilter(Filter.Installed)} 125 109 /> 126 110 <MenuCheckboxItem 127 111 id="l-repository" 128 112 label="Repository" 129 - checked={filter & Filter.Repository} 113 + checked={(filter & Filter.Repository) === Filter.Repository} 130 114 action={() => toggleFilter(Filter.Repository)} 131 115 /> 132 116 </MenuGroup> ··· 134 118 <MenuCheckboxItem 135 119 id="l-incompatible" 136 120 label="Show incompatible" 137 - checked={filter & Filter.Incompatible} 121 + checked={(filter & Filter.Incompatible) === Filter.Incompatible} 138 122 action={() => toggleFilter(Filter.Incompatible)} 139 123 /> 124 + <MenuCheckboxItem 125 + id="l-deprecated" 126 + label="Show deprecated" 127 + checked={(filter & Filter.Deprecated) === Filter.Deprecated} 128 + action={() => toggleFilter(Filter.Deprecated)} 129 + /> 140 130 <MenuItem 141 131 id="reset-all" 142 132 className={SortMenuClasses.clearText} 143 - label={ 144 - <Text variant="text-sm/medium" color="none"> 145 - Reset to default 146 - </Text> 147 - } 133 + label="Reset to default" 148 134 action={() => { 149 135 setFilter(defaultFilter); 150 136 closePopout(); ··· 156 142 ); 157 143 } 158 144 159 - function TagButtonPopout({ 160 - selectedTags, 161 - setSelectedTags, 162 - setPopoutRef, 163 - closePopout 164 - }: any) { 145 + function TagButtonPopout({ selectedTags, setSelectedTags, setPopoutRef, closePopout }: any) { 165 146 return ( 166 - <Dialog ref={setPopoutRef} className={FilterDialogClasses.container}> 167 - <div className={FilterDialogClasses.header}> 168 - <div className={FilterDialogClasses.headerLeft}> 169 - <Heading 170 - color="interactive-normal" 171 - variant="text-xs/bold" 172 - className={FilterDialogClasses.headerText} 173 - > 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}> 174 151 Select tags 175 152 </Heading> 176 - <div className={FilterDialogClasses.countContainer}> 177 - <Text 178 - className={FilterDialogClasses.countText} 179 - color="none" 180 - variant="text-xs/medium" 181 - > 153 + <div className={HeaderClasses.countContainer}> 154 + <Text className={HeaderClasses.countText} color="none" variant="text-xs/medium"> 182 155 {selectedTags.size} 183 156 </Text> 184 157 </div> 185 158 </div> 186 159 </div> 187 - <div className={FilterDialogClasses.tagContainer}> 160 + <div className={HeaderClasses.tagContainer}> 188 161 {Object.keys(tagNames).map((tag) => ( 189 162 <TagItem 190 163 key={tag} 191 - className={FilterDialogClasses.tag} 192 - 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] }} 193 166 onClick={() => toggleTag(selectedTags, setSelectedTags, tag)} 194 167 selected={selectedTags.has(tag)} 195 168 /> 196 169 ))} 197 170 </div> 198 - <div className={FilterDialogClasses.separator} /> 171 + <div className={HeaderClasses.separator} /> 199 172 <Button 200 173 look={Button.Looks.LINK} 201 174 size={Button.Sizes.MIN} 202 175 color={Button.Colors.CUSTOM} 203 - className={FilterDialogClasses.clear} 176 + className={HeaderClasses.clear} 204 177 onClick={() => { 205 178 setSelectedTags(new Set()); 206 179 closePopout(); ··· 225 198 selectedTags: Set<string>; 226 199 setSelectedTags: (tags: Set<string>) => void; 227 200 }) { 228 - const windowSize = useStateFromStores([WindowStore], () => 229 - WindowStore.windowSize() 230 - ); 201 + const windowSize = useStateFromStores([WindowStore], () => WindowStore.windowSize()); 231 202 232 203 const tagsContainer = React.useRef<HTMLDivElement>(null); 233 204 const tagListInner = React.useRef<HTMLDivElement>(null); 234 205 const [tagsButtonOffset, setTagsButtonOffset] = React.useState(0); 206 + const [checkingUpdates, setCheckingUpdates] = React.useState(false); 207 + 235 208 React.useLayoutEffect(() => { 236 209 if (tagsContainer.current === null || tagListInner.current === null) return; 237 - const { left: containerX, top: containerY } = 238 - tagsContainer.current.getBoundingClientRect(); 210 + const { left: containerX, top: containerY } = tagsContainer.current.getBoundingClientRect(); 239 211 let offset = 0; 240 212 for (const child of tagListInner.current.children) { 241 - const { 242 - right: childX, 243 - top: childY, 244 - height 245 - } = child.getBoundingClientRect(); 213 + const { right: childX, top: childY, height } = child.getBoundingClientRect(); 246 214 if (childY - containerY > height) break; 247 215 const newOffset = childX - containerX; 248 216 if (newOffset > offset) { ··· 250 218 } 251 219 } 252 220 setTagsButtonOffset(offset); 253 - }, [windowSize]); 221 + }, [windowSize, tagsContainer.current, tagListInner.current, tagListInner.current?.getBoundingClientRect()?.width]); 254 222 255 223 return ( 256 224 <div ··· 258 226 style={{ 259 227 paddingTop: "12px" 260 228 }} 261 - className={`${FilterBarClasses.tagsContainer} ${Margins.marginBottom8}`} 229 + className={`${ForumsClasses.tagsContainer} ${Margins.marginBottom8}`} 262 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> 263 256 <Popout 264 257 renderPopout={({ closePopout }: any) => ( 265 - <FilterButtonPopout 266 - filter={filter} 267 - setFilter={setFilter} 268 - closePopout={closePopout} 269 - /> 258 + <FilterButtonPopout filter={filter} setFilter={setFilter} closePopout={closePopout} /> 270 259 )} 271 260 position="bottom" 272 261 align="left" ··· 276 265 {...props} 277 266 size={Button.Sizes.MIN} 278 267 color={Button.Colors.CUSTOM} 279 - className={FilterBarClasses.sortDropdown} 280 - innerClassName={FilterBarClasses.sortDropdownInner} 268 + className={ForumsClasses.sortDropdown} 269 + innerClassName={ForumsClasses.sortDropdownInner} 281 270 > 282 271 <ArrowsUpDownIcon size="xs" /> 283 - <Text 284 - className={FilterBarClasses.sortDropdownText} 285 - variant="text-sm/medium" 286 - color="interactive-normal" 287 - > 272 + <Text className={ForumsClasses.sortDropdownText} variant="text-sm/medium" color="interactive-normal"> 288 273 Sort & filter 289 274 </Text> 290 275 {isShown ? ( ··· 295 280 </Button> 296 281 )} 297 282 </Popout> 298 - <div className={FilterBarClasses.divider} /> 299 - <div className={FilterBarClasses.tagList}> 300 - <div ref={tagListInner} className={FilterBarClasses.tagListInner}> 283 + <div className={ForumsClasses.divider} /> 284 + <div className={ForumsClasses.tagList}> 285 + <div ref={tagListInner} className={ForumsClasses.tagListInner}> 301 286 {Object.keys(tagNames).map((tag) => ( 302 287 <TagItem 303 288 key={tag} 304 - className={FilterBarClasses.tag} 305 - tag={{ name: tagNames[tag as keyof typeof tagNames] }} 289 + className={ForumsClasses.tag} 290 + tag={{ name: tagNames[tag as keyof typeof tagNames], id: tag }} 306 291 onClick={() => toggleTag(selectedTags, setSelectedTags, tag)} 307 292 selected={selectedTags.has(tag)} 308 293 /> ··· 330 315 left: tagsButtonOffset 331 316 }} 332 317 // TODO: Use Discord's class name utility 333 - className={`${FilterBarClasses.tagsButton} ${ 334 - selectedTags.size > 0 ? FilterBarClasses.tagsButtonWithCount : "" 335 - }`} 336 - innerClassName={FilterBarClasses.tagsButtonInner} 318 + className={`${ForumsClasses.tagsButton} ${selectedTags.size > 0 ? ForumsClasses.tagsButtonWithCount : ""}`} 319 + innerClassName={ForumsClasses.tagsButtonInner} 337 320 > 338 321 {selectedTags.size > 0 ? ( 339 - <div 340 - style={{ boxSizing: "content-box" }} 341 - className={FilterBarClasses.countContainer} 342 - > 343 - <Text 344 - className={FilterBarClasses.countText} 345 - color="none" 346 - variant="text-xs/medium" 347 - > 322 + <div style={{ boxSizing: "content-box" }} className={ForumsClasses.countContainer}> 323 + <Text className={ForumsClasses.countText} color="none" variant="text-xs/medium"> 348 324 {selectedTags.size} 349 325 </Text> 350 326 </div> ··· 359 335 </Button> 360 336 )} 361 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> 362 354 </div> 363 355 ); 364 356 }
+101 -54
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
··· 6 6 import React from "@moonlight-mod/wp/react"; 7 7 import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; 8 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"; 11 19 import { ExtensionCompat } from "@moonlight-mod/core/extension/loader"; 20 + import HelpMessage from "../HelpMessage"; 12 21 13 - const SearchBar: any = Object.values( 14 - spacepack.findByCode("Messages.SEARCH", "hideSearchIcon")[0].exports 15 - )[0]; 22 + const SearchBar = spacepack.require("discord/uikit/search/SearchBar").default; 23 + 24 + const validTags: string[] = Object.values(ExtensionTag); 16 25 17 26 export default function ExtensionsPage() { 18 - const { extensions, savedFilter } = useStateFromStoresObject( 19 - [MoonbaseSettingsStore], 20 - () => { 21 - return { 22 - extensions: MoonbaseSettingsStore.extensions, 23 - savedFilter: MoonbaseSettingsStore.getExtensionConfigRaw<number>( 24 - "moonbase", 25 - "filter", 26 - defaultFilter 27 - ) 28 - }; 29 - } 30 - ); 27 + const { extensions, savedFilter } = useStateFromStoresObject([MoonbaseSettingsStore], () => { 28 + return { 29 + extensions: MoonbaseSettingsStore.extensions, 30 + savedFilter: MoonbaseSettingsStore.getExtensionConfigRaw<number>("moonbase", "filter", defaultFilter) 31 + }; 32 + }); 31 33 32 34 const [query, setQuery] = React.useState(""); 35 + const [hitUpdateAll, setHitUpdateAll] = React.useState(false); 36 + 37 + const filterState = React.useState(defaultFilter); 33 38 34 39 let filter: Filter, setFilter: (filter: Filter) => void; 35 - if ( 36 - MoonbaseSettingsStore.getExtensionConfigRaw<boolean>( 37 - "moonbase", 38 - "saveFilter", 39 - false 40 - ) 41 - ) { 40 + if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "saveFilter", false)) { 42 41 filter = savedFilter ?? defaultFilter; 43 - setFilter = (filter) => 44 - MoonbaseSettingsStore.setExtensionConfig("moonbase", "filter", filter); 42 + setFilter = (filter) => MoonbaseSettingsStore.setExtensionConfig("moonbase", "filter", filter); 45 43 } else { 46 - const state = React.useState(defaultFilter); 47 - filter = state[0]; 48 - setFilter = state[1]; 44 + filter = filterState[0]; 45 + setFilter = filterState[1]; 49 46 } 47 + 50 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 + 51 58 const sorted = Object.values(extensions).sort((a, b) => { 52 59 const aName = a.manifest.meta?.name ?? a.id; 53 60 const bName = b.manifest.meta?.name ?? b.id; ··· 60 67 ext.manifest.id?.toLowerCase().includes(query) || 61 68 ext.manifest.meta?.name?.toLowerCase().includes(query) || 62 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 + )) || 63 78 ext.manifest.meta?.description?.toLowerCase().includes(query)) && 64 - [...selectedTags.values()].every( 65 - (tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag) 66 - ) && 79 + [...selectedTags.values()].every((tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag)) && 67 80 // This seems very bad, sorry 68 81 !( 69 - (!(filter & Filter.Core) && 70 - ext.source.type === ExtensionLoadSource.Core) || 71 - (!(filter & Filter.Normal) && 72 - ext.source.type === ExtensionLoadSource.Normal) || 73 - (!(filter & Filter.Developer) && 74 - ext.source.type === ExtensionLoadSource.Developer) || 75 - (!(filter & Filter.Enabled) && 76 - MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) || 77 - (!(filter & Filter.Disabled) && 78 - !MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) || 79 - (!(filter & Filter.Installed) && 80 - ext.state !== ExtensionState.NotDownloaded) || 81 - (!(filter & Filter.Repository) && 82 - ext.state === ExtensionState.NotDownloaded) 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) 83 89 ) && 84 90 (filter & Filter.Incompatible || 85 91 ext.compat === ExtensionCompat.Compatible || 86 - (ext.compat === ExtensionCompat.InvalidApiLevel && ext.hasUpdate)) 92 + (ext.compat === ExtensionCompat.InvalidApiLevel && ext.hasUpdate)) && 93 + (filter & Filter.Deprecated || 94 + ext.manifest?.meta?.deprecated !== true || 95 + ext.state !== ExtensionState.NotDownloaded) 87 96 ); 97 + 98 + // Prioritize extensions with updates 99 + const filteredWithUpdates = filtered.filter((ext) => ext!.hasUpdate); 100 + const filteredWithoutUpdates = filtered.filter((ext) => !ext!.hasUpdate); 88 101 89 102 return ( 90 103 <> ··· 101 114 spellCheck: "false" 102 115 }} 103 116 /> 104 - <FilterBar 105 - filter={filter} 106 - setFilter={setFilter} 107 - selectedTags={selectedTags} 108 - setSelectedTags={setSelectedTags} 109 - /> 110 - {filtered.map((ext) => ( 111 - <ExtensionCard uniqueId={ext.uniqueId} key={ext.uniqueId} /> 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> 112 159 ))} 113 160 </> 114 161 );
+50 -44
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/info.tsx
··· 2 2 import { MoonbaseExtension } from "../../../types"; 3 3 4 4 import React from "@moonlight-mod/wp/react"; 5 - import * as Components from "@moonlight-mod/wp/discord/components/common/index"; 6 - import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; 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 - // FIXME: type component keys 46 - const { Text } = Components; 47 - 48 - function InfoSection({ 49 - title, 50 - children 51 - }: { 52 - title: string; 53 - children: React.ReactNode; 54 - }) { 38 + function InfoSection({ title, children }: { title: string; children: React.ReactNode }) { 55 39 return ( 56 40 <div 57 41 style={{ 58 42 marginRight: "1em" 59 43 }} 60 44 > 61 - <Text variant="eyebrow" className={UserInfoClasses.userInfoSectionHeader}> 45 + <Text variant="eyebrow" className="moonlight-card-info-header"> 62 46 {title} 63 47 </Text> 64 48 ··· 69 53 70 54 function Badge({ 71 55 color, 72 - children 56 + children, 57 + style = {}, 58 + onClick 73 59 }: { 74 60 color: string; 75 61 children: React.ReactNode; 62 + style?: React.CSSProperties; 63 + onClick?: () => void; 76 64 }) { 65 + if (onClick) style.cursor ??= "pointer"; 77 66 return ( 78 67 <span 79 - style={{ 80 - borderRadius: ".1875rem", 81 - padding: "0 0.275rem", 82 - marginRight: "0.4em", 83 - backgroundColor: color, 84 - color: "#fff" 85 - }} 68 + className="moonlight-card-badge" 69 + style={ 70 + { 71 + "--badge-color": color, 72 + ...style 73 + } as React.CSSProperties 74 + } 75 + onClick={onClick} 86 76 > 87 77 {children} 88 78 </span> 89 79 ); 90 80 } 91 81 92 - 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 + }) { 93 89 const authors = ext.manifest?.meta?.authors; 94 90 const tags = ext.manifest?.meta?.tags; 95 91 const version = ext.manifest?.version; 96 92 97 93 const dependencies: Dependency[] = []; 94 + const incompatible: Dependency[] = []; 95 + 98 96 if (ext.manifest.dependencies != null) { 99 97 dependencies.push( 100 98 ...ext.manifest.dependencies.map((dep) => ({ ··· 114 112 } 115 113 116 114 if (ext.manifest.incompatible != null) { 117 - dependencies.push( 115 + incompatible.push( 118 116 ...ext.manifest.incompatible.map((dep) => ({ 119 117 id: dep, 120 118 type: DependencyType.Incompatible ··· 152 150 <InfoSection title="Tags"> 153 151 {tags.map((tag, i) => { 154 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 + } 155 159 156 160 return ( 157 - <Badge 158 - key={i} 159 - color={ 160 - tag === ExtensionTag.DangerZone 161 - ? "var(--red-400)" 162 - : "var(--brand-500)" 163 - } 164 - > 161 + <Badge key={i} color={color} style={style} onClick={() => selectTag(tag)}> 165 162 {name} 166 163 </Badge> 167 164 ); ··· 172 169 {dependencies.length > 0 && ( 173 170 <InfoSection title="Dependencies"> 174 171 {dependencies.map((dep) => { 175 - const colors = { 176 - [DependencyType.Dependency]: "var(--brand-500)", 177 - [DependencyType.Optional]: "var(--orange-400)", 178 - [DependencyType.Incompatible]: "var(--red-400)" 179 - }; 180 - const color = colors[dep.type]; 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) => { 181 187 const name = MoonbaseSettingsStore.tryGetExtensionName(dep.id); 182 188 183 189 return ( 184 - <Badge color={color} key={dep.id}> 190 + <Badge color="var(--bg-mod-strong)" key={dep.id}> 185 191 {name} 186 192 </Badge> 187 193 );
+79 -46
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/popup.tsx
··· 1 1 // TODO: clean up the styling here 2 - import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; 3 2 import React from "@moonlight-mod/wp/react"; 4 3 import { MoonbaseExtension } from "core-extensions/src/moonbase/types"; 5 - import * as Components from "@moonlight-mod/wp/discord/components/common/index"; 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 6 import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores"; 7 7 import { ExtensionLoadSource } from "@moonlight-mod/types"; 8 8 import Flex from "@moonlight-mod/wp/discord/uikit/Flex"; 9 + import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; 9 10 10 - const { 11 - openModalLazy, 12 - closeModal 13 - } = require("@moonlight-mod/wp/discord/components/common/index"); 14 - const Popup = spacepack.findByCode(".minorContainer", "secondaryAction")[0] 15 - .exports.default; 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 + } 16 24 17 25 const presentableLoadSources: Record<ExtensionLoadSource, string> = { 18 26 [ExtensionLoadSource.Developer]: "Local extension", // should never show up ··· 31 39 option: string | undefined; 32 40 setOption: (pick: string | undefined) => void; 33 41 }) { 34 - const { SingleSelect } = Components; 35 - 36 42 return ( 37 43 <SingleSelect 38 44 key={id} ··· 42 48 return { 43 49 value: candidate.uniqueId.toString(), 44 50 label: 45 - candidate.source.url ?? 46 - presentableLoadSources[candidate.source.type] ?? 47 - candidate.manifest.version ?? 48 - "" 51 + candidate.source.url ?? presentableLoadSources[candidate.source.type] ?? candidate.manifest.version ?? "" 49 52 }; 50 53 })} 51 54 onChange={(value: string) => { 52 55 setOption(value); 53 56 }} 54 - // @ts-expect-error no thanks 55 57 placeholder="Missing extension" 56 58 /> 57 59 ); 58 60 } 59 61 60 - function OurPopup({ 62 + function MissingExtensionPopup({ 61 63 deps, 62 - transitionState, 63 - id 64 + transitionState 64 65 }: { 65 66 deps: Record<string, MoonbaseExtension[]>; 66 67 transitionState: number | null; 67 - id: string; 68 68 }) { 69 - const { Text } = Components; 70 - 71 - const amountNotAvailable = Object.values(deps).filter( 72 - (candidates) => candidates.length === 0 73 - ).length; 69 + lazyLoad(); 70 + const amountNotAvailable = Object.values(deps).filter((candidates) => candidates.length === 0).length; 74 71 75 - const [options, setOptions] = React.useState< 76 - Record<string, string | undefined> 77 - >( 72 + const [options, setOptions] = React.useState<Record<string, string | undefined>>( 78 73 Object.fromEntries( 79 74 Object.entries(deps).map(([id, candidates]) => [ 80 75 id, ··· 84 79 ); 85 80 86 81 return ( 87 - <Popup 82 + <ConfirmModal 88 83 body={ 89 84 <Flex 90 85 style={{ ··· 93 88 direction={Flex.Direction.VERTICAL} 94 89 > 95 90 <Text variant="text-md/normal"> 96 - This extension depends on other extensions which are not downloaded. 97 - Choose which extensions to download. 91 + This extension depends on other extensions which are not downloaded. Choose which extensions to download. 98 92 </Text> 99 93 100 94 {amountNotAvailable > 0 && ( 101 95 <Text variant="text-md/normal"> 102 96 {amountNotAvailable} extension 103 - {amountNotAvailable > 1 ? "s" : ""} could not be found, and must 104 - be installed manually. 97 + {amountNotAvailable > 1 ? "s" : ""} could not be found, and must be installed manually. 105 98 </Text> 106 99 )} 107 100 ··· 142 135 } 143 136 cancelText="Cancel" 144 137 confirmText="Install" 145 - onCancel={() => { 146 - closeModal(id); 147 - }} 138 + onCancel={close} 148 139 onConfirm={() => { 149 - closeModal(id); 140 + close(); 150 141 151 142 for (const pick of Object.values(options)) { 152 143 if (pick != null) { ··· 160 151 ); 161 152 } 162 153 163 - export async function doPopup(deps: Record<string, MoonbaseExtension[]>) { 164 - const id: string = await openModalLazy(async () => { 165 - // eslint-disable-next-line react/display-name 154 + export async function doMissingExtensionPopup(deps: Record<string, MoonbaseExtension[]>) { 155 + await openModalLazy(async () => { 166 156 return ({ transitionState }: { transitionState: number | null }) => { 167 - return <OurPopup transitionState={transitionState} deps={deps} id={id} />; 157 + return <MissingExtensionPopup transitionState={transitionState} deps={deps} />; 168 158 }; 169 159 }); 170 160 } 171 161 172 - export default async function installWithDependencyPopup(uniqueId: number) { 173 - await MoonbaseSettingsStore.installExtension(uniqueId); 174 - const deps = await MoonbaseSettingsStore.getDependencies(uniqueId); 175 - if (deps != null) { 176 - await doPopup(deps); 177 - } 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 + }); 178 211 }
+104 -145
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
··· 11 11 12 12 import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; 13 13 import React from "@moonlight-mod/wp/react"; 14 - import * as Components from "@moonlight-mod/wp/discord/components/common/index"; 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"; 15 32 import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux"; 16 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 + ); 17 51 18 52 type SettingsProps = { 19 53 ext: MoonbaseExtension; ··· 21 55 setting: ExtensionSettingsManifest; 22 56 disabled: boolean; 23 57 }; 24 - 25 58 type SettingsComponent = React.ComponentType<SettingsProps>; 26 59 27 - import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores"; 28 60 const Margins = spacepack.require("discord/styles/shared/Margins.css"); 29 61 62 + function markdownify(str: string) { 63 + return MarkupUtils.parse(str, true, { 64 + hideSimpleEmbedContent: true, 65 + allowLinks: true 66 + }); 67 + } 68 + 30 69 function useConfigEntry<T>(uniqueId: number, name: string) { 31 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 } = Components; 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 ··· 62 91 onChange={(value: boolean) => { 63 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 } = Components; 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.id, 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 } = Components; 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.id, 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 } = Components; 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.id, 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 } = Components; 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 186 MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value); ··· 178 191 } 179 192 180 193 function MultiSelect({ ext, name, setting, disabled }: SettingsProps) { 181 - const { FormItem, FormText, Select, useVariableSelect, multiSelect } = 182 - Components; 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.id, 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 - 223 - // FIXME: type component keys 224 - const { CircleXIcon } = Components; 225 - 226 - function RemoveEntryButton({ 227 - onClick, 228 - disabled 229 - }: { 230 - onClick: () => void; 231 - disabled: boolean; 232 - }) { 233 - const { Tooltip, Clickable } = Components; 220 + function RemoveEntryButton({ onClick, disabled }: { onClick: () => void; disabled: boolean }) { 234 221 return ( 235 - <div className={RemoveButtonClasses.removeButtonContainer}> 222 + <div className={GuildSettingsRoleEditClasses.removeButtonContainer}> 236 223 <Tooltip text="Remove entry" position="top"> 237 224 {(props: any) => ( 238 - <Clickable 239 - {...props} 240 - className={RemoveButtonClasses.removeButton} 241 - onClick={onClick} 242 - > 225 + <Clickable {...props} className={GuildSettingsRoleEditClasses.removeButton} onClick={onClick}> 243 226 <CircleXIcon width={16} height={16} /> 244 227 </Clickable> 245 228 )} ··· 249 232 } 250 233 251 234 function List({ ext, name, setting, disabled }: SettingsProps) { 252 - const { FormItem, FormText, TextInput, Button } = Components; 253 - const { value, displayName, description } = useConfigEntry<string[]>( 254 - ext.uniqueId, 255 - name 256 - ); 235 + const { value, displayName, description } = useConfigEntry<string[]>(ext.uniqueId, name); 257 236 258 237 const entries = value ?? []; 259 - const updateConfig = () => 260 - MoonbaseSettingsStore.setExtensionConfig(ext.id, name, entries); 238 + const updateConfig = () => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, entries); 261 239 262 240 return ( 263 241 <FormItem className={Margins.marginTop20} title={displayName}> 264 - {description && ( 265 - <FormText className={Margins.marginBottom4}>{description}</FormText> 266 - )} 242 + {description && <FormText className={Margins.marginBottom4}>{markdownify(description)}</FormText>} 267 243 <Flex direction={Flex.Direction.VERTICAL}> 268 244 {entries.map((val, i) => ( 269 245 // FIXME: stylesheets ··· 315 291 } 316 292 317 293 function Dictionary({ ext, name, setting, disabled }: SettingsProps) { 318 - const { FormItem, FormText, TextInput, Button } = Components; 319 - const { value, displayName, description } = useConfigEntry< 320 - Record<string, string> 321 - >(ext.uniqueId, name); 294 + const { value, displayName, description } = useConfigEntry<Record<string, string>>(ext.uniqueId, name); 322 295 323 296 const entries = Object.entries(value ?? {}); 324 - const updateConfig = () => 325 - MoonbaseSettingsStore.setExtensionConfig( 326 - ext.id, 327 - name, 328 - Object.fromEntries(entries) 329 - ); 297 + const updateConfig = () => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, Object.fromEntries(entries)); 330 298 331 299 return ( 332 300 <FormItem className={Margins.marginTop20} title={displayName}> 333 - {description && ( 334 - <FormText className={Margins.marginBottom4}>{description}</FormText> 335 - )} 301 + {description && <FormText className={Margins.marginBottom4}>{markdownify(description)}</FormText>} 336 302 <Flex direction={Flex.Direction.VERTICAL}> 337 303 {entries.map(([key, val], i) => ( 338 304 // FIXME: stylesheets ··· 399 365 [MoonbaseSettingsStore], 400 366 () => { 401 367 return { 402 - component: MoonbaseSettingsStore.getExtensionConfigComponent( 403 - ext.id, 404 - name 405 - ) 368 + component: MoonbaseSettingsStore.getExtensionConfigComponent(ext.id, name) 406 369 }; 407 370 }, 408 371 [ext.uniqueId, name] 409 372 ); 410 373 411 374 if (Component == null) { 412 - const { Text } = Components; 413 375 return ( 414 - <Text variant="text/md/normal">{`Custom setting "${displayName}" is missing a component. Perhaps the extension is not installed?`}</Text> 376 + <Text variant="text-md/normal">{`Custom setting "${displayName}" is missing a component. Perhaps the extension is not installed?`}</Text> 415 377 ); 416 378 } 417 379 418 380 return ( 419 - <Component 420 - value={value} 421 - setValue={(value) => 422 - MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value) 423 - } 424 - /> 381 + <ErrorBoundary> 382 + <Component value={value} setValue={(value) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)} /> 383 + </ErrorBoundary> 425 384 ); 426 385 } 427 386 ··· 445 404 export default function Settings({ ext }: { ext: MoonbaseExtension }) { 446 405 return ( 447 406 <Flex className="moonbase-settings" direction={Flex.Direction.VERTICAL}> 448 - {Object.entries(ext.manifest.settings!).map(([name, setting]) => ( 407 + {Object.entries(ext.settingsOverride ?? ext.manifest.settings!).map(([name, setting]) => ( 449 408 <Setting 450 409 ext={ext} 451 410 key={name}
+23 -29
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
··· 1 1 import React from "@moonlight-mod/wp/react"; 2 - import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; 3 - import { 4 - Text, 5 - TabBar 6 - } from "@moonlight-mod/wp/discord/components/common/index"; 2 + import { Text, TabBar } from "@moonlight-mod/wp/discord/components/common/index"; 7 3 import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux"; 8 4 import { UserSettingsModalStore } from "@moonlight-mod/wp/common_stores"; 9 5 10 6 import ExtensionsPage from "./extensions"; 11 7 import ConfigPage from "./config"; 8 + import AboutPage from "./about"; 12 9 import Update from "./update"; 13 - 14 - const { Divider } = spacepack.findByCode(".forumOrHome]:")[0].exports.Z; 15 - const TitleBarClasses = spacepack.findByCode("iconWrapper:", "children:")[0] 16 - .exports; 17 - const TabBarClasses = spacepack.findByCode("nowPlayingColumn:")[0].exports; 18 - const { setSection, clearSubsection } = spacepack.findByExports( 19 - "setSection", 20 - "clearSubsection" 21 - )[0].exports.Z; 22 - const Margins = spacepack.require("discord/styles/shared/Margins.css"); 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"; 23 16 24 17 export const pages: { 25 18 id: string; ··· 35 28 id: "config", 36 29 name: "Config", 37 30 element: ConfigPage 31 + }, 32 + { 33 + id: "about", 34 + name: "About", 35 + element: AboutPage 38 36 } 39 37 ]; 40 38 41 39 export function Moonbase(props: { initialTab?: number } = {}) { 42 - const subsection = useStateFromStores( 43 - [UserSettingsModalStore], 44 - () => UserSettingsModalStore.getSubsection() ?? 0 45 - ); 40 + const subsection = useStateFromStores([UserSettingsModalStore], () => UserSettingsModalStore.getSubsection() ?? 0); 46 41 const setSubsection = React.useCallback( 47 42 (to: string) => { 48 - if (subsection !== to) setSection("moonbase", to); 43 + if (subsection !== to) UserSettingsModalActionCreators.setSection("moonbase", to); 49 44 }, 50 45 [subsection] 51 46 ); ··· 53 48 React.useEffect( 54 49 () => () => { 55 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 56 - clearSubsection("moonbase"); 51 + UserSettingsModalActionCreators.clearSubsection("moonbase"); 57 52 }, 58 53 [] 59 54 ); 60 55 61 56 return ( 62 57 <> 63 - <div className={`${TitleBarClasses.children} ${Margins.marginBottom20}`}> 64 - <Text 65 - className={TitleBarClasses.titleWrapper} 66 - variant="heading-lg/semibold" 67 - tag="h2" 68 - > 58 + <div className={`${HeaderBarClasses.children} ${Margins.marginBottom20}`}> 59 + <Text className={HeaderBarClasses.titleWrapper} variant="heading-lg/semibold" tag="h2"> 69 60 Moonbase 70 61 </Text> 71 62 <Divider /> ··· 73 64 selectedItem={subsection} 74 65 onItemSelect={setSubsection} 75 66 type="top-pill" 76 - className={TabBarClasses.tabBar} 67 + className={PeoplePageClasses.tabBar} 77 68 > 78 69 {pages.map((page, i) => ( 79 - <TabBar.Item key={page.id} id={i} className={TabBarClasses.item}> 70 + <TabBar.Item key={page.id} id={i} className={PeoplePageClasses.item}> 80 71 {page.name} 81 72 </TabBar.Item> 82 73 ))} 83 74 </TabBar> 84 75 </div> 85 76 77 + <RestartAdviceMessage /> 86 78 <Update /> 87 79 88 80 {React.createElement(pages[subsection].element)} 89 81 </> 90 82 ); 91 83 } 84 + 85 + export { RestartAdviceMessage, Update };
+104 -67
packages/core-extensions/src/moonbase/webpackModules/ui/update.tsx
··· 1 1 import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux"; 2 2 import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores"; 3 - import * as Components from "@moonlight-mod/wp/discord/components/common/index"; 4 3 import React from "@moonlight-mod/wp/react"; 5 - import spacepack from "@moonlight-mod/wp/spacepack_spacepack"; 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"; 6 8 import Flex from "@moonlight-mod/wp/discord/uikit/Flex"; 7 - 8 - enum UpdateState { 9 - Ready, 10 - Working, 11 - Installed, 12 - Failed 13 - } 14 - 15 - const { ThemeDarkIcon, Text, Button } = Components; 16 - const Margins = spacepack.require("discord/styles/shared/Margins.css"); 17 - const HelpMessageClasses = spacepack.findByExports("positive", "iconDiv")[0] 18 - .exports; 19 - 20 - const logger = moonlight.getLogger("moonbase/ui/update"); 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"; 21 22 22 23 const strings: Record<UpdateState, string> = { 23 24 [UpdateState.Ready]: "A new version of moonlight is available.", 24 25 [UpdateState.Working]: "Updating moonlight...", 25 26 [UpdateState.Installed]: "Updated. Restart Discord to apply changes.", 26 - [UpdateState.Failed]: 27 - "Failed to update moonlight. Please use the installer instead." 27 + [UpdateState.Failed]: "Failed to update moonlight. Please use the installer instead." 28 28 }; 29 29 30 - export default function Update() { 31 - const [state, setState] = React.useState(UpdateState.Ready); 32 - const newVersion = useStateFromStores( 33 - [MoonbaseSettingsStore], 34 - () => MoonbaseSettingsStore.newVersion 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> 35 64 ); 65 + } 66 + 67 + export default function Update() { 68 + const [newVersion, state] = useStateFromStores([MoonbaseSettingsStore], () => [ 69 + MoonbaseSettingsStore.newVersion, 70 + MoonbaseSettingsStore.updateState 71 + ]); 36 72 37 73 if (newVersion == null) return null; 38 74 39 - // reimpl of HelpMessage but with a custom icon 40 75 return ( 41 - <div 42 - className={`${Margins.marginBottom20} ${HelpMessageClasses.info} ${HelpMessageClasses.container} moonbase-update-section`} 43 - > 44 - <Flex direction={Flex.Direction.HORIZONTAL}> 45 - <div 46 - className={HelpMessageClasses.iconDiv} 47 - style={{ 48 - alignItems: "center" 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(); 49 117 }} 50 118 > 51 - <ThemeDarkIcon 52 - size="sm" 53 - color="currentColor" 54 - className={HelpMessageClasses.icon} 55 - /> 56 - </div> 57 - 58 - <Text 59 - variant="text-sm/medium" 60 - color="currentColor" 61 - className={HelpMessageClasses.text} 62 - > 63 - {strings[state]} 64 - </Text> 65 - </Flex> 66 - 67 - <Button 68 - look={Button.Looks.OUTLINED} 69 - color={Button.Colors.CUSTOM} 70 - size={Button.Sizes.TINY} 71 - disabled={state !== UpdateState.Ready} 72 - onClick={() => { 73 - setState(UpdateState.Working); 74 - 75 - MoonbaseSettingsStore.updateMoonlight() 76 - .then(() => setState(UpdateState.Installed)) 77 - .catch((e) => { 78 - logger.error(e); 79 - setState(UpdateState.Failed); 80 - }); 81 - }} 82 - > 83 - Update 84 - </Button> 85 - </div> 119 + Update 120 + </Button> 121 + </div> 122 + </HelpMessage> 86 123 ); 87 124 }
+9 -42
packages/core-extensions/src/moonbase/webpackModules/updates.tsx
··· 3 3 import Notices from "@moonlight-mod/wp/notices_notices"; 4 4 import { MoonlightBranch } from "@moonlight-mod/types"; 5 5 import React from "@moonlight-mod/wp/react"; 6 - import * as Components from "@moonlight-mod/wp/discord/components/common/index"; 7 - 8 - // FIXME: not indexed as importable 9 - const Constants = spacepack.require("discord/Constants"); 10 - const UserSettingsSections = spacepack.findObjectFromKey( 11 - Constants, 12 - "APPEARANCE_THEME_PICKER" 13 - ); 14 - 15 - const { ThemeDarkIcon } = Components; 6 + import ThemeDarkIcon from "@moonlight-mod/wp/moonbase_ThemeDarkIcon"; 16 7 17 8 function plural(str: string, num: number) { 18 9 return `${str}${num > 1 ? "s" : ""}`; ··· 21 12 function listener() { 22 13 if ( 23 14 MoonbaseSettingsStore.shouldShowNotice && 24 - MoonbaseSettingsStore.getExtensionConfigRaw( 25 - "moonbase", 26 - "updateBanner", 27 - true 28 - ) 15 + MoonbaseSettingsStore.getExtensionConfigRaw("moonbase", "updateBanner", true) 29 16 ) { 30 - // @ts-expect-error epic type fail 31 17 MoonbaseSettingsStore.removeChangeListener(listener); 32 18 33 19 const version = MoonbaseSettingsStore.newVersion; 34 - const extensionUpdateCount = Object.keys( 35 - MoonbaseSettingsStore.updates 36 - ).length; 20 + const extensionUpdateCount = Object.keys(MoonbaseSettingsStore.updates).length; 37 21 const hasExtensionUpdates = extensionUpdateCount > 0; 38 22 39 23 let message; ··· 73 57 { 74 58 name: "Open Moonbase", 75 59 onClick: () => { 76 - const { open } = spacepack.findByExports( 77 - "setSection", 78 - "clearSubsection" 79 - )[0].exports.Z; 80 - 81 - // settings is lazy loaded thus lazily patched 82 - // FIXME: figure out a way to detect if settings has been opened 83 - // alreadyjust so the transition isnt as jarring 84 - open(UserSettingsSections.ACCOUNT); 85 - setTimeout(() => { 86 - if ( 87 - MoonbaseSettingsStore.getExtensionConfigRaw<boolean>( 88 - "moonbase", 89 - "sections", 90 - false 91 - ) 92 - ) { 93 - open("moonbase-extensions"); 94 - } else { 95 - open("moonbase", 0); 96 - } 97 - }, 0); 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 + } 98 66 return true; 99 67 } 100 68 } ··· 103 71 } 104 72 } 105 73 106 - // @ts-expect-error epic type fail 107 74 MoonbaseSettingsStore.addChangeListener(listener);
+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 + }
+150 -43
packages/core-extensions/src/nativeFixes/host.ts
··· 1 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"; 2 6 3 - const enabledFeatures = app.commandLine 4 - .getSwitchValue("enable-features") 5 - .split(","); 7 + const logger = moonlightHost.getLogger("nativeFixes/host"); 8 + const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(","); 6 9 7 10 moonlightHost.events.on("window-created", function (browserWindow) { 8 - if ( 9 - moonlightHost.getConfigOption<boolean>("nativeFixes", "devtoolsThemeFix") ?? 10 - true 11 - ) { 11 + if (moonlightHost.getConfigOption<boolean>("nativeFixes", "devtoolsThemeFix") ?? true) { 12 12 browserWindow.webContents.on("devtools-opened", () => { 13 13 if (!nativeTheme.shouldUseDarkColors) return; 14 14 nativeTheme.themeSource = "light"; ··· 19 19 } 20 20 }); 21 21 22 - if ( 23 - moonlightHost.getConfigOption<boolean>( 24 - "nativeFixes", 25 - "disableRendererBackgrounding" 26 - ) ?? 27 - true 28 - ) { 22 + if (moonlightHost.getConfigOption<boolean>("nativeFixes", "disableRendererBackgrounding") ?? true) { 29 23 // Discord already disables UseEcoQoSForBackgroundProcess and some other 30 24 // related features 31 25 app.commandLine.appendSwitch("disable-renderer-backgrounding"); ··· 35 29 app.commandLine.appendSwitch("disable-background-timer-throttling"); 36 30 } 37 31 32 + if (moonlightHost.getConfigOption<boolean>("nativeFixes", "vulkan") ?? false) { 33 + enabledFeatures.push("Vulkan", "DefaultANGLEVulkan", "VulkanFromANGLE"); 34 + } 35 + 38 36 if (process.platform === "linux") { 39 - if ( 40 - moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxAutoscroll") ?? 41 - false 42 - ) { 43 - app.commandLine.appendSwitch( 44 - "enable-blink-features", 45 - "MiddleClickAutoscroll" 46 - ); 37 + if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxAutoscroll") ?? false) { 38 + app.commandLine.appendSwitch("enable-blink-features", "MiddleClickAutoscroll"); 47 39 } 48 40 49 - if ( 50 - moonlightHost.getConfigOption<boolean>( 51 - "nativeFixes", 52 - "linuxSpeechDispatcher" 53 - ) ?? 54 - true 55 - ) { 41 + if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxSpeechDispatcher") ?? true) { 56 42 app.commandLine.appendSwitch("enable-speech-dispatcher"); 57 43 } 44 + 45 + if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxHevcSupport") ?? true) { 46 + enabledFeatures.push("PlatformHEVCDecoderSupport"); 47 + } 58 48 } 59 49 60 50 // NOTE: Only tested if this appears on Windows, it should appear on all when 61 51 // hardware acceleration is disabled 62 52 const noAccel = app.commandLine.hasSwitch("disable-gpu-compositing"); 63 - if ( 64 - (moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapi") ?? true) && 65 - !noAccel 66 - ) { 67 - if (process.platform === "linux") 53 + if ((moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapi") ?? true) && !noAccel) { 54 + if (process.platform === "linux") { 68 55 // These will eventually be renamed https://source.chromium.org/chromium/chromium/src/+/5482210941a94d70406b8da962426e4faca7fce4 69 - enabledFeatures.push( 70 - "VaapiVideoEncoder", 71 - "VaapiVideoDecoder", 72 - "VaapiVideoDecodeLinuxGL" 73 - ); 56 + enabledFeatures.push("VaapiVideoEncoder", "VaapiVideoDecoder", "VaapiVideoDecodeLinuxGL"); 57 + 58 + if (moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapiIgnoreDriverChecks") ?? false) 59 + enabledFeatures.push("VaapiIgnoreDriverChecks"); 60 + } 74 61 } 75 62 76 - app.commandLine.appendSwitch( 77 - "enable-features", 78 - [...new Set(enabledFeatures)].join(",") 79 - ); 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 + }
+36 -1
packages/core-extensions/src/nativeFixes/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "nativeFixes", 3 4 "meta": { 4 5 "name": "Native Fixes", 5 6 "tagline": "Various configurable fixes for Discord and Electron", 6 - "authors": ["Cynosphere", "adryd"], 7 + "authors": ["Cynosphere", "adryd", "NotNite"], 7 8 "tags": ["fixes"] 8 9 }, 10 + "environment": "desktop", 9 11 "settings": { 10 12 "devtoolsThemeFix": { 13 + "advice": "restart", 11 14 "displayName": "Devtools Theme Fix", 12 15 "description": "Temporary workaround for devtools defaulting to light theme on Electron 32", 13 16 "type": "boolean", 14 17 "default": true 15 18 }, 16 19 "disableRendererBackgrounding": { 20 + "advice": "restart", 17 21 "displayName": "Disable Renderer Backgrounding", 18 22 "description": "This is enabled by default as a power saving measure, but it breaks screensharing and websocket connections fairly often", 19 23 "type": "boolean", 20 24 "default": true 21 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 + }, 22 33 "linuxAutoscroll": { 34 + "advice": "restart", 23 35 "displayName": "Enable middle click autoscroll on Linux", 24 36 "description": "Requires manual configuration of your system to disable middle click paste, has no effect on other operating systems", 25 37 "type": "boolean", 26 38 "default": false 27 39 }, 28 40 "linuxSpeechDispatcher": { 41 + "advice": "restart", 29 42 "displayName": "Enable speech-dispatcher for TTS on Linux", 30 43 "description": "Fixes text-to-speech. Has no effect on other operating systems", 31 44 "type": "boolean", 32 45 "default": true 33 46 }, 34 47 "vaapi": { 48 + "advice": "restart", 35 49 "displayName": "Enable VAAPI features on Linux", 36 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", 37 72 "type": "boolean", 38 73 "default": true 39 74 }
+3 -3
packages/core-extensions/src/noHideToken/index.ts
··· 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 ];
+1
packages/core-extensions/src/noHideToken/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "noHideToken", 3 4 "apiLevel": 2, 4 5 "meta": {
+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 {
+4 -1
packages/core-extensions/src/noTrack/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "noTrack", 3 4 "apiLevel": 2, 4 5 "meta": { ··· 9 10 }, 10 11 "blocked": [ 11 12 "https://*.discord.com/api/v*/science", 12 - "https://*.discord.com/api/v*/metrics" 13 + "https://*.discord.com/api/v*/metrics", 14 + "https://*.discordapp.com/api/v*/science", 15 + "https://*.discordapp.com/api/v*/metrics" 13 16 ] 14 17 }
+2 -6
packages/core-extensions/src/notices/index.ts
··· 4 4 { 5 5 find: ".GUILD_RAID_NOTIFICATION:", 6 6 replace: { 7 - match: 8 - /(?<=return(\(0,.\.jsx\))\(.+?\);)case .{1,2}\..{1,3}\.GUILD_RAID_NOTIFICATION:/, 7 + match: /(?<=return(\(0,.\.jsx\))\(.+?\);)case .{1,2}\..{1,3}\.GUILD_RAID_NOTIFICATION:/, 9 8 replacement: (orig, createElement) => 10 9 `case "__moonlight_notice":return${createElement}(require("notices_component").default,{});${orig}` 11 10 } ··· 28 27 29 28 export const webpackModules: Record<string, ExtensionWebpackModule> = { 30 29 notices: { 31 - dependencies: [ 32 - { id: "discord/packages/flux" }, 33 - { id: "discord/Dispatcher" } 34 - ] 30 + dependencies: [{ id: "discord/packages/flux" }, { id: "discord/Dispatcher" }] 35 31 }, 36 32 37 33 component: {
+1
packages/core-extensions/src/notices/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "notices", 3 4 "apiLevel": 2, 4 5 "meta": {
+4 -10
packages/core-extensions/src/notices/webpackModules/component.tsx
··· 1 1 import React from "@moonlight-mod/wp/react"; 2 2 import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher"; 3 - import * as Components from "@moonlight-mod/wp/discord/components/common/index"; 3 + import { Notice, NoticeCloseButton, PrimaryCTANoticeButton } from "@moonlight-mod/wp/discord/components/common/index"; 4 4 import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux"; 5 5 import NoticesStore from "@moonlight-mod/wp/notices_notices"; 6 - import type { Notice } from "@moonlight-mod/types/coreExtensions/notices"; 6 + import type { Notice as NoticeType } from "@moonlight-mod/types/coreExtensions/notices"; 7 7 8 - // FIXME: types 9 - const { Notice, NoticeCloseButton, PrimaryCTANoticeButton } = Components; 10 - 11 - function popAndDismiss(notice: Notice) { 8 + function popAndDismiss(notice: NoticeType) { 12 9 NoticesStore.popNotice(); 13 10 if (notice?.onDismiss) { 14 11 notice.onDismiss(); ··· 32 29 {notice.element} 33 30 34 31 {(notice.showClose ?? true) && ( 35 - <NoticeCloseButton 36 - onClick={() => popAndDismiss(notice)} 37 - noticeType="__moonlight_notice" 38 - /> 32 + <NoticeCloseButton onClick={() => popAndDismiss(notice)} noticeType="__moonlight_notice" /> 39 33 )} 40 34 41 35 {(notice.buttons ?? []).map((button) => (
+1 -4
packages/core-extensions/src/notices/webpackModules/notices.ts
··· 1 1 import { Store } from "@moonlight-mod/wp/discord/packages/flux"; 2 2 import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher"; 3 - import type { 4 - Notice, 5 - Notices 6 - } from "@moonlight-mod/types/coreExtensions/notices"; 3 + import type { Notice, Notices } from "@moonlight-mod/types/coreExtensions/notices"; 7 4 8 5 // very lazy way of doing this, FIXME 9 6 let open = false;
+95 -37
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 ··· 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. 32 - [ 33 - "is not a valid locale", 34 - /(.)\.error\(""\.concat\((.)," is not a valid locale\."\)\)/g 35 - ], 36 - ['="RunningGameStore"', /.\.info\("games",{.+?}\),/], 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"\),/], 37 49 [ 38 - '"[BUILD INFO] Release Channel: "', 39 - /new .{1,2}\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?"\)\),/ 50 + '"Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. Action: "', 51 + /\i\.has\(\i\.type\)&&\i\.log\(.+?\.type\)\),/ 40 52 ], 53 + ['console.warn("Window state not initialized"', /console\.warn\("Window state not initialized",\i\),/], 54 + ['.name="MaxListenersExceededWarning",', /(?<=\.length),\i\(\i\)/], 41 55 [ 42 - '.APP_NATIVE_CRASH,"Storage"', 43 - /console\.log\("AppCrashedFatalReport lastCrash:",.,.\);/ 56 + '"The answer for life the universe and everything is:"', 57 + /\i\.info\("The answer for life the universe and everything is:",\i\),/ 44 58 ], 45 59 [ 46 - '.APP_NATIVE_CRASH,"Storage"', 47 - 'console.log("AppCrashedFatalReport: getLastCrash not supported.");' 60 + '"isLibdiscoreBlockedDomainsEnabled called but libdiscore is not loaded"', 61 + /,\i\.verbose\("isLibdiscoreBlockedDomainsEnabledThisSession: ".concat\(\i\)\)/ 48 62 ], 49 - ['"[NATIVE INFO] ', /new .{1,2}\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/], 50 - ['"Spellchecker"', /.\.info\("Switching to ".+?"\(unavailable\)"\);?/g], 51 63 [ 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 }))
+9
packages/core-extensions/src/quietLoggers/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "quietLoggers", 3 4 "apiLevel": 2, 4 5 "meta": { ··· 9 10 }, 10 11 "settings": { 11 12 "xssDefensesOnly": { 13 + "advice": "reload", 12 14 "displayName": "Only hide self-XSS", 13 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)", 14 23 "type": "boolean", 15 24 "default": false 16 25 }
+46 -67
packages/core-extensions/src/rocketship/host/permissions.ts
··· 14 14 details: Electron.PermissionCheckHandlerHandlerDetails 15 15 ) => boolean; 16 16 17 - moonlightHost.events.on( 18 - "window-created", 19 - (window: BrowserWindow, isMainWindow: boolean) => { 20 - if (!isMainWindow) return; 21 - const windowSession = window.webContents.session; 17 + moonlightHost.events.on("window-created", (window: BrowserWindow, isMainWindow: boolean) => { 18 + if (!isMainWindow) return; 19 + const windowSession = window.webContents.session; 22 20 23 - // setPermissionRequestHandler 24 - windowSession.setPermissionRequestHandler( 25 - (webcontents, permission, callback, details) => { 26 - let cbResult = false; 27 - function fakeCallback(result: boolean) { 28 - cbResult = result; 29 - } 21 + // setPermissionRequestHandler 22 + windowSession.setPermissionRequestHandler((webcontents, permission, callback, details) => { 23 + let cbResult = false; 24 + function fakeCallback(result: boolean) { 25 + cbResult = result; 26 + } 30 27 31 - if (caughtPermissionRequestHandler) { 32 - caughtPermissionRequestHandler( 33 - webcontents, 34 - permission, 35 - fakeCallback, 36 - details 37 - ); 38 - } 28 + if (caughtPermissionRequestHandler) { 29 + caughtPermissionRequestHandler(webcontents, permission, fakeCallback, details); 30 + } 39 31 40 - if (permission === "media" || permission === "display-capture") { 41 - cbResult = true; 42 - } 32 + if (permission === "media" || permission === "display-capture") { 33 + cbResult = true; 34 + } 43 35 44 - callback(cbResult); 45 - } 46 - ); 36 + callback(cbResult); 37 + }); 47 38 48 - let caughtPermissionRequestHandler: PermissionRequestHandler | undefined; 39 + let caughtPermissionRequestHandler: PermissionRequestHandler | undefined; 49 40 50 - windowSession.setPermissionRequestHandler = 51 - function catchSetPermissionRequestHandler( 52 - handler: ( 53 - webcontents: Electron.WebContents, 54 - permission: string, 55 - callback: (permissionGranted: boolean) => void 56 - ) => void 57 - ) { 58 - caughtPermissionRequestHandler = handler; 59 - }; 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 + }; 60 50 61 - // setPermissionCheckHandler 62 - windowSession.setPermissionCheckHandler( 63 - (webcontents, permission, requestingOrigin, details) => { 64 - return false; 65 - } 66 - ); 51 + // setPermissionCheckHandler 52 + windowSession.setPermissionCheckHandler((webcontents, permission, requestingOrigin, details) => { 53 + return false; 54 + }); 67 55 68 - let caughtPermissionCheckHandler: PermissionCheckHandler | undefined; 56 + let caughtPermissionCheckHandler: PermissionCheckHandler | undefined; 69 57 70 - windowSession.setPermissionCheckHandler( 71 - (webcontents, permission, requestingOrigin, details) => { 72 - let result = false; 58 + windowSession.setPermissionCheckHandler((webcontents, permission, requestingOrigin, details) => { 59 + let result = false; 73 60 74 - if (caughtPermissionCheckHandler) { 75 - result = caughtPermissionCheckHandler( 76 - webcontents, 77 - permission, 78 - requestingOrigin, 79 - details 80 - ); 81 - } 61 + if (caughtPermissionCheckHandler) { 62 + result = caughtPermissionCheckHandler(webcontents, permission, requestingOrigin, details); 63 + } 82 64 83 - if (permission === "media" || permission === "display-capture") { 84 - result = true; 85 - } 65 + if (permission === "media" || permission === "display-capture") { 66 + result = true; 67 + } 86 68 87 - return result; 88 - } 89 - ); 69 + return result; 70 + }); 90 71 91 - windowSession.setPermissionCheckHandler = 92 - function catchSetPermissionCheckHandler(handler: PermissionCheckHandler) { 93 - caughtPermissionCheckHandler = handler; 94 - }; 95 - } 96 - ); 72 + windowSession.setPermissionCheckHandler = function catchSetPermissionCheckHandler(handler: PermissionCheckHandler) { 73 + caughtPermissionCheckHandler = handler; 74 + }; 75 + });
+4 -12
packages/core-extensions/src/rocketship/host/types.ts
··· 1 1 // https://github.com/Vencord/venmic/blob/d737ef33eaae7a73d03ec02673e008cf0243434d/lib/module.d.ts 2 2 type DefaultProps = "node.name" | "application.name"; 3 3 4 - type LiteralUnion<LiteralType, BaseType extends string> = 5 - | LiteralType 6 - | (BaseType & Record<never, never>); 4 + type LiteralUnion<LiteralType, BaseType extends string> = LiteralType | (BaseType & Record<never, never>); 7 5 8 - type Optional<Type, Key extends keyof Type> = Partial<Pick<Type, Key>> & 9 - Omit<Type, Key>; 6 + type Optional<Type, Key extends keyof Type> = Partial<Pick<Type, Key>> & Omit<Type, Key>; 10 7 11 - export type Node<T extends string = never> = Record< 12 - LiteralUnion<T, string>, 13 - string 14 - >; 8 + export type Node<T extends string = never> = Record<LiteralUnion<T, string>, string>; 15 9 16 10 export interface LinkData { 17 11 include: Node[]; ··· 29 23 unlink(): void; 30 24 31 25 list<T extends string = DefaultProps>(props?: T[]): Node<T>[]; 32 - link( 33 - data: Optional<LinkData, "exclude"> | Optional<LinkData, "include"> 34 - ): boolean; 26 + link(data: Optional<LinkData, "exclude"> | Optional<LinkData, "include">): boolean; 35 27 }
+23 -31
packages/core-extensions/src/rocketship/host/venmic.ts
··· 7 7 8 8 function getPatchbay() { 9 9 try { 10 - const venmic = require( 11 - path.join(path.dirname(moonlightHost.asarPath), "..", "venmic.node") 12 - ) as { PatchBay: new () => PatchBay }; 10 + const venmic = require(path.join(path.dirname(moonlightHost.asarPath), "..", "venmic.node")) as { 11 + PatchBay: new () => PatchBay; 12 + }; 13 13 const patchbay = new venmic.PatchBay(); 14 14 return patchbay; 15 15 } catch (error) { ··· 35 35 36 36 patchbay.unlink(); 37 37 return patchbay.link({ 38 - exclude: [ 39 - { "application.process.id": pid }, 40 - { "media.class": "Stream/Input/Audio" } 41 - ], 38 + exclude: [{ "application.process.id": pid }, { "media.class": "Stream/Input/Audio" }], 42 39 ignore_devices: true, 43 40 only_speakers: true, 44 41 only_default_speakers: true ··· 49 46 } 50 47 } 51 48 52 - moonlightHost.events.on( 53 - "window-created", 54 - (window: BrowserWindow, isMainWindow: boolean) => { 55 - if (!isMainWindow) return; 56 - const windowSession = window.webContents.session; 49 + moonlightHost.events.on("window-created", (window: BrowserWindow, isMainWindow: boolean) => { 50 + if (!isMainWindow) return; 51 + const windowSession = window.webContents.session; 57 52 58 - // @ts-expect-error these types ancient 59 - windowSession.setDisplayMediaRequestHandler( 60 - (request: any, callback: any) => { 61 - const linked = linkVenmic(); 62 - desktopCapturer 63 - .getSources({ types: ["screen", "window"] }) 64 - .then((sources) => { 65 - //logger.debug("desktopCapturer.getSources", sources); 66 - logger.debug("Linked to venmic:", linked); 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); 67 60 68 - callback({ 69 - video: sources[0], 70 - audio: "loopback" 71 - }); 72 - }); 73 - }, 74 - { useSystemPicker: true } 75 - ); 76 - } 77 - ); 61 + callback({ 62 + video: sources[0], 63 + audio: "loopback" 64 + }); 65 + }); 66 + }, 67 + { useSystemPicker: true } 68 + ); 69 + });
+5 -11
packages/core-extensions/src/rocketship/index.ts
··· 9 9 logger.debug("Devices:", devices); 10 10 11 11 // This isn't vencord :( 12 - const id = devices.find((device) => device.label === "vencord-screen-share") 13 - ?.deviceId; 12 + const id = devices.find((device) => device.label === "vencord-screen-share")?.deviceId; 14 13 if (!id) return null; 15 14 logger.debug("Got venmic device ID:", id); 16 15 ··· 32 31 } 33 32 } 34 33 35 - navigator.mediaDevices.getDisplayMedia = async function getDisplayMediaRedirect( 36 - options 37 - ) { 34 + navigator.mediaDevices.getDisplayMedia = async function getDisplayMediaRedirect(options) { 38 35 const orig = await getDisplayMediaOrig.call(this, options); 39 36 40 37 const venmic = await getVenmicStream(); ··· 114 111 replace: [ 115 112 // Prevent loading of krisp native module by stubbing out desktop checks 116 113 { 117 - match: 118 - /\(\(0,.\.isWindows\)\(\)\|\|\(0,.\.isLinux\)\(\)\|\|.+?&&!__OVERLAY__/, 114 + match: /\(\(0,.\.isWindows\)\(\)\|\|\(0,.\.isLinux\)\(\)\|\|.+?&&!__OVERLAY__/, 119 115 replacement: (orig, macosPlatformCheck) => `false&&!__OVERLAY__` 120 116 }, 121 117 // Enable loading of web krisp equivelant by replacing isWeb with true 122 118 { 123 - match: 124 - /\(0,.\.isWeb\)\(\)&&(.{1,2}\.supports\(.{1,2}\..{1,2}.NOISE_CANCELLATION)/, 125 - replacement: (orig, supportsNoiseCancellation) => 126 - `true&&${supportsNoiseCancellation}` 119 + match: /\(0,.\.isWeb\)\(\)&&(.{1,2}\.supports\(.{1,2}\..{1,2}.NOISE_CANCELLATION)/, 120 + replacement: (orig, supportsNoiseCancellation) => `true&&${supportsNoiseCancellation}` 127 121 } 128 122 ] 129 123 }
+4 -1
packages/core-extensions/src/rocketship/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "rocketship", 3 4 "apiLevel": 2, 5 + "environment": "desktop", 4 6 "meta": { 5 7 "name": "Rocketship", 6 8 "tagline": "Adds new features when using rocketship", 7 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.", 8 - "authors": ["NotNite", "Cynosphere", "adryd"] 10 + "authors": ["NotNite", "Cynosphere", "adryd"], 11 + "deprecated": true 9 12 } 10 13 }
+3 -5
packages/core-extensions/src/settings/index.ts
··· 6 6 find: '"useGenerateUserSettingsSections"', 7 7 replace: { 8 8 match: /(?<=\.push\(.+?\)}\)\)}\),)(.+?)}/, 9 - replacement: (_, sections: string) => 10 - `require("settings_settings").Settings._mutateSections(${sections})}` 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 }
+1
packages/core-extensions/src/settings/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "settings", 3 4 "apiLevel": 2, 4 5 "meta": {
+11 -13
packages/core-extensions/src/settings/webpackModules/settings.ts
··· 1 - import { 2 - SettingsSection, 3 - Settings as SettingsType 4 - } from "@moonlight-mod/types/coreExtensions/settings"; 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;
+2
packages/core-extensions/src/spacepack/manifest.json
··· 1 1 { 2 + "$schema": "https://moonlight-mod.github.io/manifest.schema.json", 2 3 "id": "spacepack", 3 4 "apiLevel": 2, 4 5 "meta": { ··· 9 10 }, 10 11 "settings": { 11 12 "addToGlobalScope": { 13 + "advice": "reload", 12 14 "displayName": "Add to global scope", 13 15 "description": "Populates window.spacepack for easier usage in DevTools", 14 16 "type": "boolean",
+84 -67
packages/core-extensions/src/spacepack/webpackModules/spacepack.ts
··· 1 - import { 2 - WebpackModule, 3 - WebpackModuleFunc, 4 - WebpackRequireType 5 - } from "@moonlight-mod/types"; 1 + import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "@moonlight-mod/types"; 6 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; ··· 40 37 "module", 41 38 "exports", 42 39 "require", 43 - `(${funcStr}).apply(this, arguments)\n` + 44 - `//# sourceURL=Webpack-Module-${module}` 40 + `(${funcStr}).apply(this, arguments)\n` + `//# sourceURL=Webpack-Module/${module.slice(0, 3)}/${module}` 45 41 ) as WebpackModuleFunc; 46 42 }, 47 43 48 44 findByCode: (...args: (string | RegExp)[]) => { 49 - return Object.entries(modules) 50 - .filter( 51 - ([id, mod]) => 52 - !args.some( 53 - (item) => 54 - !(item instanceof RegExp 55 - ? item.test(mod.toString()) 56 - : mod.toString().indexOf(item) !== -1) 57 - ) 58 - ) 45 + const ret = Object.entries(modules) 46 + .filter(([id, mod]) => !args.some((item) => !testFind(mod.toString(), processFind(item)))) 59 47 .map(([id]) => { 60 48 //if (!(id in cache)) require(id); 61 49 //return cache[id]; ··· 64 52 try { 65 53 exports = require(id); 66 54 } catch (e) { 67 - logger.error(`Error requiring module "${id}": `, e); 55 + logger.error(`findByCode: Error requiring module "${id}": `, args, e); 68 56 } 69 57 70 58 return { ··· 73 61 }; 74 62 }) 75 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; 76 70 }, 77 71 78 72 findByExports: (...args: string[]) => { ··· 84 78 !( 85 79 exports !== undefined && 86 80 exports !== window && 87 - (exports?.[item] || 88 - exports?.default?.[item] || 89 - exports?.Z?.[item] || 90 - exports?.ZP?.[item]) 81 + (exports?.[item] || exports?.default?.[item] || exports?.Z?.[item] || exports?.ZP?.[item]) 91 82 ) 92 83 ) 93 84 ) ··· 99 90 }, 100 91 101 92 findObjectFromKey: (exports: Record<string, any>, key: string) => { 93 + let ret = null; 102 94 let subKey; 103 95 if (key.indexOf(".") > -1) { 104 96 const splitKey = key.split("."); ··· 109 101 const obj = exports[exportKey]; 110 102 if (obj && obj[key] !== undefined) { 111 103 if (subKey) { 112 - if (obj[key][subKey]) return obj; 104 + if (obj[key][subKey]) { 105 + ret = obj; 106 + break; 107 + } 113 108 } else { 114 - return obj; 109 + ret = obj; 110 + break; 115 111 } 116 112 } 117 113 } 118 - 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; 119 120 }, 120 121 121 122 findObjectFromValue: (exports: Record<string, any>, value: any) => { 123 + let ret = null; 122 124 for (const exportKey in exports) { 123 125 const obj = exports[exportKey]; 124 126 // eslint-disable-next-line eqeqeq 125 - if (obj == value) return obj; 127 + if (obj == value) { 128 + ret = obj; 129 + break; 130 + } 126 131 for (const subKey in obj) { 127 132 // eslint-disable-next-line eqeqeq 128 133 if (obj && obj[subKey] == value) { 129 - return obj; 134 + ret = obj; 135 + break; 130 136 } 131 137 } 132 138 } 133 - 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; 134 145 }, 135 146 136 - findObjectFromKeyValuePair: ( 137 - exports: Record<string, any>, 138 - key: string, 139 - value: any 140 - ) => { 147 + findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => { 148 + let ret = null; 141 149 for (const exportKey in exports) { 142 150 const obj = exports[exportKey]; 143 151 // eslint-disable-next-line eqeqeq 144 152 if (obj && obj[key] == value) { 145 - return obj; 153 + ret = obj; 154 + break; 146 155 } 147 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 + 148 169 return null; 149 170 }, 150 171 151 - findFunctionByStrings: ( 152 - exports: Record<string, any>, 153 - ...strings: (string | RegExp)[] 154 - ) => { 155 - return ( 172 + findFunctionByStrings: (exports: Record<string, any>, ...strings: (string | RegExp)[]) => { 173 + const ret = 156 174 Object.entries(exports).filter( 157 175 ([index, func]) => 158 - typeof func === "function" && 159 - !strings.some( 160 - (query) => 161 - !(query instanceof RegExp 162 - ? func.toString().match(query) 163 - : func.toString().includes(query)) 164 - ) 165 - )?.[0]?.[1] ?? null 166 - ); 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; 167 184 }, 168 185 169 - lazyLoad: ( 170 - find: string | RegExp | (string | RegExp)[], 171 - chunk: RegExp, 172 - module: RegExp 173 - ) => { 174 - const mod = Array.isArray(find) 175 - ? spacepack.findByCode(...find) 176 - : spacepack.findByCode(find); 177 - 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 + } 178 195 179 196 const findId = mod[0].id; 180 197 const findCode = webpackRequire.m[findId].toString().replace(/\n/g, ""); ··· 184 201 chunkIds = [...findCode.matchAll(chunk)].map(([, id]) => id); 185 202 } else { 186 203 const match = findCode.match(chunk); 187 - if (match) 188 - chunkIds = [...match[0].matchAll(/"(\d+)"/g)].map(([, id]) => id); 204 + if (match) chunkIds = [...match[0].matchAll(/"(\d+)"/g)].map(([, id]) => id); 189 205 } 190 206 191 - 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)); 192 209 return Promise.reject("Chunk ID match failed"); 210 + } 193 211 194 212 const moduleId = findCode.match(module)?.[1]; 195 - 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 + } 196 217 197 - return Promise.all(chunkIds.map((c) => webpackRequire.e(c))).then(() => 198 - webpackRequire(moduleId) 199 - ); 218 + return Promise.all(chunkIds.map((c) => webpackRequire.e(c))).then(() => webpackRequire(moduleId)); 200 219 }, 201 220 202 221 filterReal: (modules: WebpackModule[]) => { ··· 204 223 } 205 224 }; 206 225 207 - if ( 208 - moonlight.getConfigOption<boolean>("spacepack", "addToGlobalScope") === true 209 - ) { 226 + if (moonlight.getConfigOption<boolean>("spacepack", "addToGlobalScope") === true) { 210 227 window.spacepack = spacepack; 211 228 } 212 229
+4 -1
packages/core-extensions/tsconfig.json
··· 1 1 { 2 - "extends": "../../tsconfig.json" 2 + "extends": "../../tsconfig.json", 3 + "compilerOptions": { 4 + "lib": ["ESNext", "DOM", "DOM.Iterable"] 5 + } 3 6 }
+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 }
+126 -119
packages/injector/src/index.ts
··· 6 6 } from "electron"; 7 7 import Module from "node:module"; 8 8 import { constants, MoonlightBranch } from "@moonlight-mod/types"; 9 - import { readConfig } from "@moonlight-mod/core/config"; 9 + import { readConfig, writeConfig } from "@moonlight-mod/core/config"; 10 10 import { getExtensions } from "@moonlight-mod/core/extension"; 11 11 import Logger, { initLogger } from "@moonlight-mod/core/util/logger"; 12 - import { 13 - loadExtensions, 14 - loadProcessedExtensions 15 - } from "@moonlight-mod/core/extension/loader"; 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"; 18 15 import persist from "@moonlight-mod/core/persist"; 19 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"; 20 19 21 20 const logger = new Logger("injector"); 22 21 23 22 let oldPreloadPath: string | undefined; 24 23 let corsAllow: string[] = []; 25 24 let blockedUrls: RegExp[] = []; 26 - let isMoonlightDesktop = false; 27 - let hasOpenAsar = false; 28 - let openAsarConfigPreload: string | undefined; 25 + let injectorConfig: InjectorConfig | undefined; 26 + 27 + const scriptUrls = ["web.", "sentry."]; 28 + const blockedScripts = new Set<string>(); 29 29 30 30 ipcMain.on(constants.ipcGetOldPreloadPath, (e) => { 31 31 e.returnValue = oldPreloadPath; 32 32 }); 33 + 33 34 ipcMain.on(constants.ipcGetAppData, (e) => { 34 35 e.returnValue = app.getPath("appData"); 35 36 }); 36 - ipcMain.on(constants.ipcGetIsMoonlightDesktop, (e) => { 37 - e.returnValue = isMoonlightDesktop; 37 + ipcMain.on(constants.ipcGetInjectorConfig, (e) => { 38 + e.returnValue = injectorConfig; 38 39 }); 39 40 ipcMain.handle(constants.ipcMessageBox, (_, opts) => { 40 41 electron.dialog.showMessageBoxSync(opts); ··· 44 45 }); 45 46 46 47 const reEscapeRegExp = /[\\^$.*+?()[\]{}|]/g; 47 - const reMatchPattern = 48 - /^(?<scheme>\*|[a-z][a-z0-9+.-]*):\/\/(?<host>.+?)\/(?<path>.+)?$/; 48 + const reMatchPattern = /^(?<scheme>\*|[a-z][a-z0-9+.-]*):\/\/(?<host>.+?)\/(?<path>.+)?$/; 49 49 50 50 const escapeRegExp = (s: string) => s.replace(reEscapeRegExp, "\\$&"); 51 51 ipcMain.handle(constants.ipcSetBlockedList, (_, list: string[]) => { ··· 76 76 blockedUrls = compiled; 77 77 }); 78 78 79 - function patchCsp(headers: Record<string, string[]>) { 80 - const directives = [ 81 - "style-src", 82 - "connect-src", 83 - "img-src", 84 - "font-src", 85 - "media-src", 86 - "worker-src", 87 - "prefetch-src" 88 - ]; 89 - const values = ["*", "blob:", "data:", "'unsafe-inline'", "disclip:"]; 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:"]; 90 82 91 83 const csp = "content-security-policy"; 92 84 if (headers[csp] == null) return; ··· 105 97 parts[directive] = values; 106 98 } 107 99 100 + for (const [directive, urls] of Object.entries(extensionCspOverrides)) { 101 + parts[directive] ??= []; 102 + parts[directive].push(...urls); 103 + } 104 + 108 105 const stringified = Object.entries<string[]>(parts) 109 106 .map(([key, value]) => { 110 107 return `${key} ${value.join(" ")}`; ··· 113 110 headers[csp] = [stringified]; 114 111 } 115 112 116 - function removeOpenAsarEventIfPresent(eventHandler: (...args: any[]) => void) { 117 - const code = eventHandler.toString(); 118 - if (code.indexOf("bw.webContents.on('dom-ready'") > -1) { 119 - electron.app.off("browser-window-created", eventHandler); 120 - } 121 - } 122 - 123 113 class BrowserWindow extends ElectronBrowserWindow { 124 114 constructor(opts: BrowserWindowConstructorOptions) { 125 - oldPreloadPath = opts.webPreferences!.preload; 115 + const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1; 126 116 127 - const isMainWindow = 128 - opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1; 129 - 130 - if (isMainWindow) 117 + if (isMainWindow) { 118 + if (!oldPreloadPath) oldPreloadPath = opts.webPreferences!.preload; 131 119 opts.webPreferences!.preload = require.resolve("./node-preload.js"); 120 + } 132 121 133 122 // Event for modifying window options 134 123 moonlightHost.events.emit("window-options", opts, isMainWindow); ··· 138 127 // Event for when a window is created 139 128 moonlightHost.events.emit("window-created", this, isMainWindow); 140 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 + } 141 + 141 142 this.webContents.session.webRequest.onHeadersReceived((details, cb) => { 142 143 if (details.responseHeaders != null) { 143 144 // Patch CSP so things can use externally hosted assets 144 145 if (details.resourceType === "mainFrame") { 145 - patchCsp(details.responseHeaders); 146 + patchCsp(details.responseHeaders, extensionCspOverrides); 146 147 } 147 148 148 149 // Allow plugins to bypass CORS for specific URLs 149 150 if (corsAllow.some((x) => details.url.startsWith(x))) { 150 - 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] = ["*"]; 151 158 } 159 + 160 + moonlightHost.events.emit("headers-received", details, isMainWindow); 152 161 153 162 cb({ cancel: false, responseHeaders: details.responseHeaders }); 154 163 } 155 164 }); 156 165 157 - // Allow plugins to block some URLs, 158 - // this is needed because multiple webRequest handlers cannot be registered at once 159 166 this.webContents.session.webRequest.onBeforeRequest((details, cb) => { 160 - cb({ cancel: blockedUrls.some((u) => u.test(details.url)) }); 161 - }); 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. 162 173 163 - if (hasOpenAsar) { 164 - // Remove DOM injections 165 - // Settings can still be opened via: 166 - // `DiscordNative.ipc.send("DISCORD_UPDATED_QUOTES","o")` 167 - // @ts-expect-error Electron internals 168 - const events = electron.app._events["browser-window-created"]; 169 - if (Array.isArray(events)) { 170 - for (const event of events) { 171 - removeOpenAsarEventIfPresent(event); 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); 172 195 } 173 - } else if (events != null) { 174 - removeOpenAsarEventIfPresent(events); 175 - } 176 196 177 - // Config screen fails to context bridge properly 178 - // Less than ideal, but better than disabling it everywhere 179 - if (opts.webPreferences!.preload === openAsarConfigPreload) { 180 - opts.webPreferences!.sandbox = false; 197 + if (hasUrl) return cb({ cancel: true }); 181 198 } 182 - } 199 + 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 + }); 183 204 } 184 205 } 185 206 ··· 200 221 writable: false 201 222 }); 202 223 203 - export async function inject(asarPath: string) { 204 - isMoonlightDesktop = asarPath === "moonlightDesktop"; 205 - global.moonlightFS = createFS(); 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 + }; 206 234 207 235 try { 208 - const config = await readConfig(); 236 + let config = await readConfig(); 209 237 initLogger(config); 210 238 const extensions = await getExtensions(); 239 + const processedExtensions = await loadExtensions(extensions); 240 + const moonlightDir = await getMoonlightDir(); 241 + const extensionsPath = await getExtensionsPath(); 211 242 212 243 // Duplicated in node-preload... oops 213 - // eslint-disable-next-line no-inner-declarations 214 244 function getConfig(ext: string) { 215 245 const val = config.extensions[ext]; 216 246 if (val == null || typeof val === "boolean") return undefined; 217 247 return val.config; 218 248 } 219 - 220 249 global.moonlightHost = { 250 + get config() { 251 + return config; 252 + }, 253 + extensions, 254 + processedExtensions, 221 255 asarPath, 222 - config, 223 256 events: new EventEmitter(), 224 - extensions, 225 - processedExtensions: { 226 - extensions: [], 227 - dependencyGraph: new Map() 228 - }, 229 257 230 258 version: MOONLIGHT_VERSION, 231 259 branch: MOONLIGHT_BRANCH as MoonlightBranch, 232 260 233 261 getConfig, 234 - getConfigOption: <T>(ext: string, name: string) => { 235 - const config = getConfig(ext); 236 - if (config == null) return undefined; 237 - const option = config[name]; 238 - if (option == null) return undefined; 239 - return option as T; 262 + getConfigPath, 263 + getConfigOption(ext, name) { 264 + const manifest = getManifest(extensions, ext); 265 + return getConfigOption(ext, name, config, manifest?.settings); 240 266 }, 241 - 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) { 242 277 return new Logger(id); 278 + }, 279 + getMoonlightDir() { 280 + return moonlightDir; 281 + }, 282 + getExtensionDir: (ext: string) => { 283 + return path.join(extensionsPath, ext); 243 284 } 244 285 }; 245 286 246 - // Check if we're running with OpenAsar 247 - try { 248 - require.resolve(join(asarPath, "updater", "updater.js")); 249 - hasOpenAsar = true; 250 - openAsarConfigPreload = resolve(asarPath, "config", "preload.js"); 251 - // eslint-disable-next-line no-empty 252 - } catch {} 253 - 254 - if (hasOpenAsar) { 255 - // Disable command line switch injection 256 - // I personally think that the command line switches should be vetted by 257 - // the user and not just "trust that these are sane defaults that work 258 - // always". I'm not hating on Ducko or anything, I'm just opinionated. 259 - // Someone can always make a command line modifier plugin, thats the point 260 - // of having host modules. 261 - try { 262 - const cmdSwitchesPath = require.resolve( 263 - join(asarPath, "cmdSwitches.js") 264 - ); 265 - require.cache[cmdSwitchesPath] = new Module( 266 - cmdSwitchesPath, 267 - require.cache[require.resolve(asarPath)] 268 - ); 269 - require.cache[cmdSwitchesPath]!.exports = () => {}; 270 - } catch (error) { 271 - logger.error("Failed to disable OpenAsar's command line flags:", error); 272 - } 273 - } 274 - 275 287 patchElectron(); 276 288 277 - global.moonlightHost.processedExtensions = await loadExtensions(extensions); 278 289 await loadProcessedExtensions(global.moonlightHost.processedExtensions); 279 290 } catch (error) { 280 291 logger.error("Failed to inject:", error); 281 292 } 282 293 283 - if (isMoonlightDesktop) return; 284 - 285 - if (!hasOpenAsar && !isMoonlightDesktop) { 294 + if (injectorConfig?.disablePersist !== true) { 286 295 persist(asarPath); 287 296 } 288 297 289 - // Need to do this instead of require() or it breaks require.main 290 - // @ts-expect-error Module internals 291 - 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 + } 292 303 } 293 304 294 305 function patchElectron() { ··· 302 313 configurable: false 303 314 }); 304 315 } else { 305 - Object.defineProperty( 306 - electronClone, 307 - property, 308 - Object.getOwnPropertyDescriptor(electron, property)! 309 - ); 316 + Object.defineProperty(electronClone, property, Object.getOwnPropertyDescriptor(electron, property)!); 310 317 } 311 318 } 312 319
+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 }
+121 -44
packages/node-preload/src/index.ts
··· 5 5 import { readConfig, writeConfig } from "@moonlight-mod/core/config"; 6 6 import { constants, MoonlightBranch } from "@moonlight-mod/types"; 7 7 import { getExtensions } from "@moonlight-mod/core/extension"; 8 - import { 9 - getExtensionsPath, 10 - getMoonlightDir 11 - } from "@moonlight-mod/core/util/data"; 8 + import { getExtensionsPath, getMoonlightDir } from "@moonlight-mod/core/util/data"; 12 9 import Logger, { initLogger } from "@moonlight-mod/core/util/logger"; 13 - import { 14 - loadExtensions, 15 - loadProcessedExtensions 16 - } from "@moonlight-mod/core/extension/loader"; 10 + import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader"; 17 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 + } 18 25 19 26 async function injectGlobals() { 20 - global.moonlightFS = createFS(); 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 + }; 21 38 22 - const config = await readConfig(); 39 + let config = await readConfig(); 23 40 initLogger(config); 41 + logger = new Logger("node-preload"); 42 + 24 43 const extensions = await getExtensions(); 25 44 const processedExtensions = await loadExtensions(extensions); 26 45 const moonlightDir = await getMoonlightDir(); 27 46 const extensionsPath = await getExtensionsPath(); 28 47 29 - function getConfig(ext: string) { 30 - const val = config.extensions[ext]; 31 - if (val == null || typeof val === "boolean") return undefined; 32 - return val.config; 33 - } 34 - 35 48 global.moonlightNode = { 36 - config, 49 + get config() { 50 + return config; 51 + }, 37 52 extensions, 38 53 processedExtensions, 39 54 nativesCache: {}, 40 55 isBrowser: false, 56 + events: createEventEmitter<NodeEventType, NodeEventPayloads>(), 41 57 42 58 version: MOONLIGHT_VERSION, 43 59 branch: MOONLIGHT_BRANCH as MoonlightBranch, 44 60 45 - getConfig, 46 - getConfigOption: <T>(ext: string, name: string) => { 47 - const config = getConfig(ext); 48 - if (config == null) return undefined; 49 - const option = config[name]; 50 - if (option == null) return undefined; 51 - return option as T; 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); 52 76 }, 77 + 53 78 getNatives: (ext: string) => global.moonlightNode.nativesCache[ext], 54 79 getLogger: (id: string) => { 55 80 return new Logger(id); 56 81 }, 57 - 58 82 getMoonlightDir() { 59 83 return moonlightDir; 60 84 }, 61 85 getExtensionDir: (ext: string) => { 62 86 return path.join(extensionsPath, ext); 63 - }, 64 - writeConfig 87 + } 65 88 }; 66 89 67 90 await loadProcessedExtensions(processedExtensions); 68 91 contextBridge.exposeInMainWorld("moonlightNode", moonlightNode); 69 92 70 - const extCors = moonlightNode.processedExtensions.extensions.flatMap( 71 - (x) => x.manifest.cors ?? [] 72 - ); 93 + const extCors = moonlightNode.processedExtensions.extensions.flatMap((x) => x.manifest.cors ?? []); 94 + for (const cors of extCors) { 95 + registerCors(cors); 96 + } 73 97 74 98 for (const repo of moonlightNode.config.repositories) { 75 99 const url = new URL(repo); 76 100 url.pathname = "/"; 77 - extCors.push(url.toString()); 101 + registerCors(url.toString()); 102 + } 103 + 104 + const extBlocked = moonlightNode.processedExtensions.extensions.flatMap((e) => e.manifest.blocked ?? []); 105 + for (const blocked of extBlocked) { 106 + registerBlocked(blocked); 78 107 } 79 108 80 - ipcRenderer.invoke(constants.ipcSetCorsList, extCors); 109 + setCors(); 81 110 82 - const extBlocked = moonlightNode.processedExtensions.extensions.flatMap( 83 - (e) => e.manifest.blocked ?? [] 84 - ); 85 - ipcRenderer.invoke(constants.ipcSetBlockedList, extBlocked); 111 + initialized = true; 86 112 } 87 113 88 114 async function loadPreload() { 89 115 const webPreloadPath = path.join(__dirname, "web-preload.js"); 90 116 const webPreload = fs.readFileSync(webPreloadPath, "utf8"); 91 117 await webFrame.executeJavaScript(webPreload); 118 + 119 + const func = await webFrame.executeJavaScript("async () => { await window._moonlightWebLoad(); }"); 120 + await func(); 92 121 } 93 122 94 - async function init(oldPreloadPath: string) { 123 + async function init() { 95 124 try { 96 125 await injectGlobals(); 97 126 await loadPreload(); ··· 102 131 message: message 103 132 }); 104 133 } 134 + } 105 135 106 - // Let Discord start even if we fail 107 - if (oldPreloadPath) require(oldPreloadPath); 108 - } 136 + const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath); 137 + const isOverlay = window.location.href.indexOf("discord_overlay") > -1; 109 138 110 - const oldPreloadPath: string = ipcRenderer.sendSync( 111 - constants.ipcGetOldPreloadPath 112 - ); 113 - init(oldPreloadPath); 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 + }); 190 + }
+4 -1
packages/node-preload/tsconfig.json
··· 1 1 { 2 - "extends": "../../tsconfig.json" 2 + "extends": "../../tsconfig.json", 3 + "compilerOptions": { 4 + "lib": ["DOM", "ESNext", "DOM.Iterable"] 5 + } 3 6 }
+14 -7
packages/types/package.json
··· 1 1 { 2 2 "name": "@moonlight-mod/types", 3 - "version": "1.3.0", 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 - "@moonlight-mod/lunast": "^1.0.0", 13 - "@moonlight-mod/mappings": "^1.0.2", 14 - "@moonlight-mod/moonmap": "^1.0.2", 19 + "@moonlight-mod/lunast": "^1.0.1", 20 + "@moonlight-mod/mappings": "^1.1.25", 21 + "@moonlight-mod/moonmap": "^1.0.5", 15 22 "@types/react": "^18.3.10", 16 - "csstype": "^3.1.2", 23 + "csstype": "^3.1.3", 17 24 "standalone-electron-types": "^1.0.0" 18 25 } 19 26 }
+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
+6 -3
packages/types/src/constants.ts
··· 4 4 export const repoUrlFile = ".moonlight-repo-url"; 5 5 export const installedVersionFile = ".moonlight-installed-version"; 6 6 7 + export const ipcNodePreloadKickoff = "_moonlight_nodePreloadKickoff"; 7 8 export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath"; 9 + 8 10 export const ipcGetAppData = "_moonlight_getAppData"; 9 - export const ipcGetIsMoonlightDesktop = "_moonlight_getIsMoonlightDesktop"; 11 + export const ipcGetInjectorConfig = "_moonlight_getInjectorConfig"; 10 12 export const ipcMessageBox = "_moonlight_messageBox"; 11 13 export const ipcSetCorsList = "_moonlight_setCorsList"; 12 14 export const ipcSetBlockedList = "_moonlight_setBlockedList"; 13 15 14 16 export const apiLevel = 2; 15 17 16 - export const mainRepo = 17 - "https://moonlight-mod.github.io/extensions-dist/repo.json"; 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"];
+17 -20
packages/types/src/core/event.ts
··· 1 + import { Config } from "../config"; 1 2 import { WebpackModuleFunc, WebpackRequireType } from "../discord"; 2 3 3 - export interface MoonlightEventEmitter< 4 - EventId extends string = string, 5 - EventData = Record<EventId, any> 6 - > { 7 - dispatchEvent: <Id extends keyof EventData>( 8 - id: Id, 9 - data: EventData[Id] 10 - ) => void; 11 - addEventListener: <Id extends keyof EventData>( 12 - id: Id, 13 - cb: (data: EventData[Id]) => void 14 - ) => void; 15 - removeEventListener: <Id extends keyof EventData>( 16 - id: Id, 17 - cb: (data: EventData[Id]) => void 18 - ) => void; 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; 19 8 } 20 9 21 - export enum EventType { 10 + export enum WebEventType { 22 11 ChunkLoad = "chunkLoad", 23 12 ExtensionLoad = "extensionLoad" 24 13 } 25 14 26 - export type EventPayloads = { 27 - [EventType.ChunkLoad]: { 15 + export type WebEventPayloads = { 16 + [WebEventType.ChunkLoad]: { 28 17 chunkId?: number[]; 29 18 modules: { [id: string]: WebpackModuleFunc }; 30 19 require?: (require: WebpackRequireType) => any; 31 20 }; 32 - [EventType.ExtensionLoad]: string; 21 + [WebEventType.ExtensionLoad]: string; 22 + }; 23 + 24 + export enum NodeEventType { 25 + ConfigSaved = "configSaved" 26 + } 27 + 28 + export type NodeEventPayloads = { 29 + [NodeEventType.ConfigSaved]: Config; 33 30 };
+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
··· 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
··· 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
··· 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
+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
··· 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 };
+12 -7
packages/types/src/coreExtensions/moonbase.ts
··· 1 - export type CustomComponent = React.FC<{ 1 + export type CustomComponentProps = { 2 2 value: any; 3 3 setValue: (value: any) => void; 4 - }>; 4 + }; 5 + 6 + export type CustomComponent = React.FC<CustomComponentProps>; 5 7 6 8 export type Moonbase = { 7 - registerConfigComponent: ( 8 - ext: string, 9 - option: string, 10 - component: CustomComponent 11 - ) => void; 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; 12 17 };
+16 -1
packages/types/src/coreExtensions/notices.ts
··· 1 - import type { Store } from "@moonlight-mod/mappings/discord/packages/flux"; 1 + import type { Store } from "@moonlight-mod/mappings/discord/packages/flux/Store"; 2 2 3 3 export type NoticeButton = { 4 4 name: string; ··· 14 14 }; 15 15 16 16 export type Notices = Store<any> & { 17 + /** 18 + * Adds a custom notice to the top of the screen. 19 + */ 17 20 addNotice: (notice: Notice) => void; 21 + 22 + /** 23 + * Removes the current notice from the top of the screen. 24 + */ 18 25 popNotice: () => void; 26 + 27 + /** 28 + * @private 29 + */ 19 30 getCurrentNotice: () => Notice | null; 31 + 32 + /** 33 + * @private 34 + */ 20 35 shouldShowNotice: () => boolean; 21 36 };
+39 -8
packages/types/src/coreExtensions/settings.ts
··· 1 1 import React, { ReactElement } from "react"; 2 - import type { Store } from "@moonlight-mod/mappings/discord/packages/flux"; 2 + import type { Store } from "@moonlight-mod/mappings/discord/packages/flux/Store"; 3 3 4 4 export type NoticeProps = { 5 5 stores: Store<any>[]; ··· 7 7 }; 8 8 9 9 export type SettingsSection = 10 - | { section: "DIVIDER"; pos: number } 11 - | { section: "HEADER"; label: string; pos: number } 10 + | { section: "DIVIDER"; pos: number | ((sections: SettingsSection[]) => number) } 11 + | { section: "HEADER"; label: string; pos: number | ((sections: SettingsSection[]) => number) } 12 12 | { 13 13 section: string; 14 14 label: string; 15 15 color: string | null; 16 16 element: React.FunctionComponent; 17 - pos: number; 17 + pos: number | ((sections: SettingsSection[]) => number); 18 18 notice?: NoticeProps; 19 + onClick?: () => void; 19 20 _moonlight_submenu?: () => ReactElement | ReactElement[]; 20 21 }; 21 22 ··· 24 25 sectionNames: string[]; 25 26 sectionMenuItems: Record<string, ReactElement[]>; 26 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 + */ 27 38 addSection: ( 28 39 section: string, 29 40 label: string, 30 41 element: React.FunctionComponent, 31 42 color?: string | null, 32 - pos?: number, 33 - notice?: NoticeProps 43 + pos?: number | ((sections: SettingsSection[]) => number), 44 + notice?: NoticeProps, 45 + onClick?: () => void 34 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 + */ 35 53 addSectionMenuItems: (section: string, ...items: ReactElement[]) => void; 36 54 37 - addDivider: (pos: number | null) => void; 38 - addHeader: (label: string, pos: number | null) => void; 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 + */ 39 70 _mutateSections: (sections: SettingsSection[]) => SettingsSection[]; 40 71 };
+83 -18
packages/types/src/coreExtensions/spacepack.ts
··· 1 - import { 2 - WebpackModule, 3 - WebpackModuleFunc, 4 - WebpackRequireType 5 - } from "../discord"; 1 + import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "../discord"; 6 2 7 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 + */ 8 10 inspect: (module: number | string) => WebpackModuleFunc | null; 9 - findByCode: (...args: (string | RegExp)[]) => any[]; 10 - findByExports: (...args: string[]) => any[]; 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 + */ 11 30 require: WebpackRequireType; 31 + 32 + /** 33 + * The Webpack module list. 34 + * Re-export of require.m. 35 + */ 12 36 modules: Record<string, WebpackModuleFunc>; 37 + 38 + /** 39 + * The Webpack module cache. 40 + * Re-export of require.c. 41 + */ 13 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 + */ 14 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 + */ 15 58 findObjectFromValue: (exports: Record<string, any>, value: any) => any | null; 16 - findObjectFromKeyValuePair: ( 17 - exports: Record<string, any>, 18 - key: string, 19 - value: any 20 - ) => 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 + */ 21 76 findFunctionByStrings: ( 22 77 exports: Record<string, any>, 23 78 ...strings: (string | RegExp)[] 24 - // eslint-disable-next-line @typescript-eslint/ban-types 79 + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type 25 80 ) => Function | null; 26 - lazyLoad: ( 27 - find: string | RegExp | (string | RegExp)[], 28 - chunk: RegExp, 29 - module: RegExp 30 - ) => Promise<any>; 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 + */ 31 96 filterReal: (modules: WebpackModule[]) => WebpackModule[]; 32 97 };
+4
packages/types/src/coreExtensions.ts
··· 4 4 export * as ContextMenu from "./coreExtensions/contextMenu"; 5 5 export * as Notices from "./coreExtensions/notices"; 6 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";
+17 -2
packages/types/src/discord/require.ts
··· 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"; 1 5 import { ContextMenu, EvilItemParser } from "../coreExtensions/contextMenu"; 2 6 import { Markdown } from "../coreExtensions/markdown"; 7 + import { Moonbase } from "../coreExtensions/moonbase"; 8 + import { Notices } from "../coreExtensions/notices"; 3 9 import { Settings } from "../coreExtensions/settings"; 4 10 import { Spacepack } from "../coreExtensions/spacepack"; 5 - import { Notices } from "../coreExtensions/notices"; 6 - import { Moonbase } from "../coreExtensions/moonbase"; 7 11 8 12 declare function WebpackRequire(id: string): any; 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; 9 24 10 25 declare function WebpackRequire(id: "contextMenu_evilMenu"): EvilItemParser; 11 26 declare function WebpackRequire(id: "contextMenu_contextMenu"): ContextMenu;
+3 -11
packages/types/src/discord/webpack.ts
··· 10 10 11 11 export type WebpackModule = { 12 12 id: string | number; 13 - loaded: boolean; 13 + loaded?: boolean; 14 14 exports: any; 15 15 }; 16 16 17 - export type WebpackModuleFunc = (( 18 - module: any, 19 - exports: any, 20 - require: WebpackRequireType 21 - ) => void) & { 17 + export type WebpackModuleFunc = ((module: any, exports: any, require: WebpackRequireType) => void) & { 22 18 __moonlight?: boolean; 23 19 }; 24 20 25 - export type WebpackJsonpEntry = [ 26 - number[], 27 - { [id: string]: WebpackModuleFunc }, 28 - (require: WebpackRequireType) => any 29 - ]; 21 + export type WebpackJsonpEntry = [number[], { [id: string]: WebpackModuleFunc }, (require: WebpackRequireType) => any]; 30 22 31 23 export type WebpackJsonp = WebpackJsonpEntry[] & { 32 24 push: {
+105 -3
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; 42 + 43 + /** 44 + * The API level this extension targets. If it does not match the current version, the extension will not be loaded. 45 + */ 33 46 apiLevel?: number; 47 + 48 + /** 49 + * Which environment this extension is capable of running in. 50 + */ 34 51 environment?: ExtensionEnvironment; 35 52 53 + /** 54 + * Metadata about your extension for use in Moonbase. 55 + */ 36 56 meta?: { 57 + /** 58 + * A human friendly name for your extension as a proper noun. 59 + */ 37 60 name?: string; 61 + 62 + /** 63 + * A short tagline that appears below the name. 64 + */ 38 65 tagline?: string; 66 + 67 + /** 68 + * A longer description that can use Markdown. 69 + */ 39 70 description?: string; 71 + 72 + /** 73 + * List of authors that worked on this extension - accepts string or object with ID. 74 + */ 40 75 authors?: ExtensionAuthor[]; 41 - deprecated?: boolean; 76 + 77 + /** 78 + * A list of tags that are relevant to the extension. 79 + */ 42 80 tags?: ExtensionTag[]; 81 + 82 + /** 83 + * The URL to the source repository. 84 + */ 43 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; 44 102 }; 45 103 104 + /** 105 + * A list of extension IDs that are required for the extension to load. 106 + */ 46 107 dependencies?: string[]; 108 + 109 + /** 110 + * A list of extension IDs that the user may want to install. 111 + */ 47 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 + */ 48 118 incompatible?: string[]; 49 119 120 + /** 121 + * A list of settings for your extension, where the key is the settings ID. 122 + */ 50 123 settings?: Record<string, ExtensionSettingsManifest>; 51 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 + */ 52 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 + */ 53 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[]>; 54 144 }; 55 145 56 146 export enum ExtensionEnvironment { 147 + /** 148 + * The extension will run on both platforms, the host/native modules MAY be loaded 149 + */ 57 150 Both = "both", 151 + 152 + /** 153 + * Extension will run on desktop only, the host/native modules are guaranteed to load 154 + */ 58 155 Desktop = "desktop", 156 + 157 + /** 158 + * Currently equivalent to Both 159 + */ 59 160 Web = "web" 60 161 } 61 162 ··· 75 176 webpackModules?: Record<string, string>; 76 177 nodePath?: string; 77 178 hostPath?: string; 179 + style?: string; 78 180 }; 79 181 }; 80 182 ··· 106 208 export type Patch = { 107 209 find: PatchMatch; 108 210 replace: PatchReplace | PatchReplace[]; 211 + hardFail?: boolean; // if any patches fail, all fail 109 212 prerequisite?: () => boolean; 110 213 }; 111 214 ··· 133 236 id: number; 134 237 }; 135 238 136 - export type IdentifiedWebpackModule = ExtensionWebpackModule & 137 - ExplicitExtensionDependency; 239 + export type IdentifiedWebpackModule = ExtensionWebpackModule & ExplicitExtensionDependency;
+2
packages/types/src/fs.ts
··· 11 11 12 12 exists: (path: string) => Promise<boolean>; 13 13 isFile: (path: string) => Promise<boolean>; 14 + isDir: (path: string) => Promise<boolean>; 14 15 15 16 join: (...parts: string[]) => string; 16 17 dirname: (path: string) => string; 18 + basename: (path: string) => string; 17 19 };
+37 -21
packages/types/src/globals.ts
··· 1 1 import type { Logger } from "./logger"; 2 2 import type { Config, ConfigExtension } from "./config"; 3 - import type { 4 - DetectedExtension, 5 - IdentifiedPatch, 6 - IdentifiedWebpackModule, 7 - ProcessedExtensions 8 - } from "./extension"; 3 + import type { DetectedExtension, IdentifiedPatch, IdentifiedWebpackModule, ProcessedExtensions } from "./extension"; 9 4 import type EventEmitter from "events"; 10 5 import type LunAST from "@moonlight-mod/lunast"; 11 6 import type Moonmap from "@moonlight-mod/moonmap"; 12 7 import type { 13 - EventPayloads, 14 - EventType, 15 - MoonlightEventEmitter 8 + WebEventPayloads, 9 + WebEventType, 10 + MoonlightEventEmitter, 11 + NodeEventType, 12 + NodeEventPayloads 16 13 } from "./core/event"; 14 + import type { MoonlightFS } from "./fs"; 17 15 18 16 export type MoonlightHost = { 19 - asarPath: string; 20 17 config: Config; 21 - events: EventEmitter; 22 18 extensions: DetectedExtension[]; 23 19 processedExtensions: ProcessedExtensions; 20 + asarPath: string; 21 + events: EventEmitter; 24 22 25 23 version: string; 26 24 branch: MoonlightBranch; 27 25 28 26 getConfig: (ext: string) => ConfigExtension["config"]; 27 + getConfigPath: () => Promise<string>; 29 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 + 30 32 getLogger: (id: string) => Logger; 33 + getMoonlightDir: () => string; 34 + getExtensionDir: (ext: string) => string; 31 35 }; 32 36 33 37 export type MoonlightNode = { ··· 36 40 processedExtensions: ProcessedExtensions; 37 41 nativesCache: Record<string, any>; 38 42 isBrowser: boolean; 43 + events: MoonlightEventEmitter<NodeEventType, NodeEventPayloads>; 39 44 40 45 version: string; 41 46 branch: MoonlightBranch; 42 47 43 48 getConfig: (ext: string) => ConfigExtension["config"]; 44 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 + 45 53 getNatives: (ext: string) => any | undefined; 46 54 getLogger: (id: string) => Logger; 47 - 48 55 getMoonlightDir: () => string; 49 56 getExtensionDir: (ext: string) => string; 50 - writeConfig: (config: Config) => Promise<void>; 57 + }; 58 + 59 + export type MoonlightNodeSandboxed = { 60 + fs: MoonlightFS; 61 + addCors: (url: string) => void; 62 + addBlocked: (url: string) => void; 51 63 }; 52 64 53 65 export type MoonlightWeb = { 66 + patched: Map<string, Set<string>>; 54 67 unpatched: Set<IdentifiedPatch>; 55 68 pendingModules: Set<IdentifiedWebpackModule>; 56 69 enabledExtensions: Set<string>; 57 - apiLevel: number; 58 - events: MoonlightEventEmitter<EventType, EventPayloads>; 70 + events: MoonlightEventEmitter<WebEventType, WebEventPayloads>; 59 71 patchingInternals: { 60 - onModuleLoad: ( 61 - moduleId: string | string[], 62 - callback: (moduleId: string) => void 63 - ) => void; 72 + onModuleLoad: (moduleId: string | string[], callback: (moduleId: string) => void) => void; 64 73 registerPatch: (patch: IdentifiedPatch) => void; 65 74 registerWebpackModule: (module: IdentifiedWebpackModule) => void; 66 75 }; 76 + localStorage: Storage; 67 77 68 78 version: string; 69 79 branch: MoonlightBranch; 80 + apiLevel: number; 70 81 71 - getConfig: (ext: string) => ConfigExtension["config"]; 72 - getConfigOption: <T>(ext: string, name: string) => T | undefined; 82 + // Re-exports for ease of use 83 + getConfig: MoonlightNode["getConfig"]; 84 + getConfigOption: MoonlightNode["getConfigOption"]; 85 + setConfigOption: MoonlightNode["setConfigOption"]; 86 + writeConfig: MoonlightNode["writeConfig"]; 87 + 73 88 getNatives: (ext: string) => any | undefined; 74 89 getLogger: (id: string) => Logger; 90 + 75 91 lunast: LunAST; 76 92 moonmap: Moonmap; 77 93 };
+38
packages/types/src/import.d.ts
··· 1 + declare module "@moonlight-mod/wp/appPanels_appPanels" { 2 + import { CoreExtensions } from "@moonlight-mod/types"; 3 + const AppPanels: CoreExtensions.AppPanels.AppPanels; 4 + export = AppPanels; 5 + } 6 + 7 + declare module "@moonlight-mod/wp/commands_commands" { 8 + import { CoreExtensions } from "@moonlight-mod/types"; 9 + export const commands: CoreExtensions.Commands.Commands; 10 + export default commands; 11 + } 12 + 13 + declare module "@moonlight-mod/wp/common_ErrorBoundary" { 14 + import { CoreExtensions } from "@moonlight-mod/types"; 15 + const ErrorBoundary: CoreExtensions.Common.ErrorBoundary; 16 + export = ErrorBoundary; 17 + } 18 + declare module "@moonlight-mod/wp/common_icons" { 19 + import { CoreExtensions } from "@moonlight-mod/types"; 20 + export const icons: CoreExtensions.Common.Icons; 21 + export default icons; 22 + } 1 23 declare module "@moonlight-mod/wp/common_stores"; 24 + 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; 39 + } 2 40 3 41 declare module "@moonlight-mod/wp/contextMenu_evilMenu" { 4 42 import { CoreExtensions } from "@moonlight-mod/types";
+5 -10
packages/types/src/index.ts
··· 4 4 /// <reference types="./mappings" /> 5 5 /* eslint-disable no-var */ 6 6 7 - import { MoonlightFS } from "./fs"; 8 - import { 9 - MoonlightEnv, 10 - MoonlightHost, 11 - MoonlightNode, 12 - MoonlightWeb 13 - } from "./globals"; 7 + import { MoonlightEnv, MoonlightHost, MoonlightNode, MoonlightNodeSandboxed, MoonlightWeb } from "./globals"; 14 8 15 9 export * from "./discord"; 16 10 export * from "./config"; ··· 36 30 37 31 var moonlightHost: MoonlightHost; 38 32 var moonlightNode: MoonlightNode; 33 + var moonlightNodeSandboxed: MoonlightNodeSandboxed; 39 34 var moonlight: MoonlightWeb; 40 - var moonlightFS: MoonlightFS; 35 + var _moonlight_coreExtensionsStr: string; 41 36 42 - var _moonlightBrowserInit: () => Promise<void>; 43 - var _moonlightBrowserLoad: () => Promise<void>; 37 + var _moonlightBrowserInit: undefined | (() => Promise<void>); 38 + var _moonlightWebLoad: undefined | (() => Promise<void>); 44 39 }
+860 -14
packages/types/src/mappings.d.ts
··· 1 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 + 2 39 declare module "@moonlight-mod/wp/discord/Dispatcher" { 3 40 import { MappedModules } from "@moonlight-mod/mappings"; 4 - const _: MappedModules["discord/Dispatcher"]; 5 - export = _; 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"]; 6 160 } 7 161 8 162 declare module "@moonlight-mod/wp/discord/components/common/index" { 9 163 import { MappedModules } from "@moonlight-mod/mappings"; 10 - const _: MappedModules["discord/components/common/index"]; 11 - export = _; 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; 12 281 } 13 282 14 - declare module "@moonlight-mod/wp/discord/modules/guild_settings/IntegrationCard.css" { 283 + declare module "@moonlight-mod/wp/discord/lib/BaseRecord" { 15 284 import { MappedModules } from "@moonlight-mod/mappings"; 16 - const _: MappedModules["discord/modules/guild_settings/IntegrationCard.css"]; 17 - export = _; 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"]; 18 503 } 19 504 20 505 declare module "@moonlight-mod/wp/discord/modules/markup/MarkupUtils" { 21 506 import { MappedModules } from "@moonlight-mod/mappings"; 22 - const _: MappedModules["discord/modules/markup/MarkupUtils"]; 23 - export = _; 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"]; 24 666 } 25 667 26 668 declare module "@moonlight-mod/wp/discord/packages/flux" { 27 669 import { MappedModules } from "@moonlight-mod/mappings"; 28 - const _: MappedModules["discord/packages/flux"]; 29 - export = _; 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"]; 30 747 } 31 748 32 749 declare module "@moonlight-mod/wp/discord/uikit/Flex" { 33 750 import { MappedModules } from "@moonlight-mod/mappings"; 34 - const _: MappedModules["discord/uikit/Flex"]; 35 - export = _; 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"]; 36 880 } 37 881 38 882 declare module "@moonlight-mod/wp/react" { 39 883 import { MappedModules } from "@moonlight-mod/mappings"; 40 - const _: MappedModules["react"]; 884 + const _: Omit<MappedModules["react"], "__mappings_exportEquals">; 41 885 export = _; 42 886 } 887 + 888 + declare module "@moonlight-mod/wp/uuid/v4" {}
+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 }
+10 -3
packages/web-preload/package.json
··· 2 2 "name": "@moonlight-mod/web-preload", 3 3 "private": true, 4 4 "main": "src/index.ts", 5 + "engineStrict": true, 6 + "engines": { 7 + "node": ">=22", 8 + "pnpm": ">=10", 9 + "npm": "pnpm", 10 + "yarn": "pnpm" 11 + }, 5 12 "dependencies": { 6 13 "@moonlight-mod/core": "workspace:*", 7 - "@moonlight-mod/lunast": "^1.0.0", 8 - "@moonlight-mod/mappings": "^1.0.2", 9 - "@moonlight-mod/moonmap": "^1.0.2", 14 + "@moonlight-mod/lunast": "catalog:prod", 15 + "@moonlight-mod/mappings": "catalog:prod", 16 + "@moonlight-mod/moonmap": "catalog:prod", 10 17 "@moonlight-mod/types": "workspace:*" 11 18 } 12 19 }
+16 -19
packages/web-preload/src/index.ts
··· 1 1 import { loadProcessedExtensions } from "@moonlight-mod/core/extension/loader"; 2 - import { 3 - installWebpackPatcher, 4 - onModuleLoad, 5 - registerPatch, 6 - registerWebpackModule 7 - } from "@moonlight-mod/core/patch"; 2 + import { installWebpackPatcher, onModuleLoad, registerPatch, registerWebpackModule } from "@moonlight-mod/core/patch"; 8 3 import { constants, MoonlightBranch } from "@moonlight-mod/types"; 9 4 import { installStyles } from "@moonlight-mod/core/styles"; 10 5 import Logger, { initLogger } from "@moonlight-mod/core/util/logger"; ··· 12 7 import Moonmap from "@moonlight-mod/moonmap"; 13 8 import loadMappings from "@moonlight-mod/mappings"; 14 9 import { createEventEmitter } from "@moonlight-mod/core/util/event"; 15 - import { EventPayloads, EventType } from "@moonlight-mod/types/core/event"; 10 + import { WebEventPayloads, WebEventType } from "@moonlight-mod/types/core/event"; 16 11 17 12 async function load() { 13 + delete window._moonlightWebLoad; 18 14 initLogger(moonlightNode.config); 19 15 const logger = new Logger("web-preload"); 20 16 21 17 window.moonlight = { 22 - apiLevel: constants.apiLevel, 18 + patched: new Map(), 23 19 unpatched: new Set(), 24 20 pendingModules: new Set(), 25 21 enabledExtensions: new Set(), 26 - events: createEventEmitter<EventType, EventPayloads>(), 22 + 23 + events: createEventEmitter<WebEventType, WebEventPayloads>(), 27 24 patchingInternals: { 28 25 onModuleLoad, 29 26 registerPatch, 30 27 registerWebpackModule 31 28 }, 29 + localStorage: window.localStorage, 32 30 33 31 version: MOONLIGHT_VERSION, 34 32 branch: MOONLIGHT_BRANCH as MoonlightBranch, 33 + apiLevel: constants.apiLevel, 35 34 36 35 getConfig: moonlightNode.getConfig.bind(moonlightNode), 37 36 getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode), 37 + setConfigOption: moonlightNode.setConfigOption.bind(moonlightNode), 38 + writeConfig: moonlightNode.writeConfig.bind(moonlightNode), 39 + 38 40 getNatives: moonlightNode.getNatives.bind(moonlightNode), 39 41 getLogger(id) { 40 42 return new Logger(id); 41 43 }, 44 + 42 45 lunast: new LunAST(), 43 46 moonmap: new Moonmap() 44 47 }; ··· 51 54 logger.error("Error setting up web-preload", e); 52 55 } 53 56 54 - if (MOONLIGHT_ENV === "web-preload") { 55 - window.addEventListener("DOMContentLoaded", () => { 56 - installStyles(); 57 - }); 57 + if (document.readyState === "complete") { 58 + installStyles(); 58 59 } else { 59 - installStyles(); 60 + window.addEventListener("load", installStyles); 60 61 } 61 62 } 62 63 63 - if (MOONLIGHT_ENV === "web-preload") { 64 - load(); 65 - } else { 66 - window._moonlightBrowserLoad = load; 67 - } 64 + window._moonlightWebLoad = load;
+4 -1
packages/web-preload/tsconfig.json
··· 1 1 { 2 - "extends": "../../tsconfig.json" 2 + "extends": "../../tsconfig.json", 3 + "compilerOptions": { 4 + "lib": ["ESNext", "DOM"] 5 + } 3 6 }
+1498 -1196
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))(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))(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 44 93 45 94 packages/browser: 46 95 dependencies: ··· 54 103 specifier: workspace:* 55 104 version: link:../web-preload 56 105 '@zenfs/core': 57 - specifier: ^1.0.2 58 - version: 1.0.2 106 + specifier: catalog:prod 107 + version: 2.0.0 59 108 '@zenfs/dom': 60 - specifier: ^0.2.16 61 - version: 0.2.16(@zenfs/core@1.0.2) 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 62 115 63 116 packages/core: 64 117 dependencies: ··· 74 127 '@moonlight-mod/types': 75 128 specifier: workspace:* 76 129 version: link:../types 130 + microdiff: 131 + specifier: catalog:prod 132 + version: 1.5.0 77 133 nanotar: 78 - specifier: ^0.1.1 134 + specifier: catalog:prod 79 135 version: 0.1.1 80 136 81 137 packages/injector: ··· 99 155 packages/types: 100 156 dependencies: 101 157 '@moonlight-mod/lunast': 102 - specifier: ^1.0.0 103 - version: 1.0.0 158 + specifier: ^1.0.1 159 + version: 1.0.1 104 160 '@moonlight-mod/mappings': 105 - specifier: ^1.0.2 106 - version: 1.0.2(@moonlight-mod/lunast@1.0.0)(@moonlight-mod/moonmap@1.0.2) 161 + specifier: ^1.1.25 162 + version: 1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5) 107 163 '@moonlight-mod/moonmap': 108 - specifier: ^1.0.2 109 - version: 1.0.2 164 + specifier: ^1.0.5 165 + version: 1.0.5 110 166 '@types/react': 111 167 specifier: ^18.3.10 112 - version: 18.3.10 168 + version: 18.3.20 113 169 csstype: 114 - specifier: ^3.1.2 115 - version: 3.1.2 170 + specifier: ^3.1.3 171 + version: 3.1.3 116 172 standalone-electron-types: 117 173 specifier: ^1.0.0 118 174 version: 1.0.0 ··· 123 179 specifier: workspace:* 124 180 version: link:../core 125 181 '@moonlight-mod/lunast': 126 - specifier: ^1.0.0 127 - version: 1.0.0 182 + specifier: catalog:prod 183 + version: 1.0.1 128 184 '@moonlight-mod/mappings': 129 - specifier: ^1.0.2 130 - version: 1.0.2(@moonlight-mod/lunast@1.0.0)(@moonlight-mod/moonmap@1.0.2) 185 + specifier: catalog:prod 186 + version: 1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5) 131 187 '@moonlight-mod/moonmap': 132 - specifier: ^1.0.2 133 - version: 1.0.2 188 + specifier: catalog:prod 189 + version: 1.0.5 134 190 '@moonlight-mod/types': 135 191 specifier: workspace:* 136 192 version: link:../types ··· 140 196 '@aashutoshrathi/word-wrap@1.2.6': 141 197 resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} 142 198 engines: {node: '>=0.10.0'} 199 + 200 + '@antfu/ni@24.3.0': 201 + resolution: {integrity: sha512-wBSav4mBxvHEW9RbdSo1SWLQ6MAlT0Dc423weC58yOWqW4OcMvtnNDdDrxOZeJ88fEIyPK93gDUWIelBxzSf8g==} 202 + hasBin: true 143 203 144 204 '@esbuild/android-arm64@0.19.3': 145 205 resolution: {integrity: sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==} ··· 273 333 cpu: [x64] 274 334 os: [win32] 275 335 276 - '@eslint-community/eslint-utils@4.4.0': 277 - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} 336 + '@eslint-community/eslint-utils@4.5.1': 337 + resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==} 278 338 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 279 339 peerDependencies: 280 340 eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 281 341 282 - '@eslint-community/regexpp@4.10.0': 283 - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} 342 + '@eslint-community/regexpp@4.12.1': 343 + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} 284 344 engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 285 345 286 - '@eslint/eslintrc@2.1.4': 287 - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} 288 - 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} 349 + 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} 353 + 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} 289 365 290 - '@eslint/js@8.55.0': 291 - resolution: {integrity: sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==} 292 - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 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} 293 369 294 - '@humanwhocodes/config-array@0.11.13': 295 - resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} 296 - engines: {node: '>=10.10.0'} 297 - deprecated: Use @eslint/config-array instead 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'} 298 385 299 386 '@humanwhocodes/module-importer@1.0.1': 300 387 resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 301 388 engines: {node: '>=12.22'} 302 389 303 - '@humanwhocodes/object-schema@2.0.1': 304 - resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} 305 - deprecated: Use @eslint/object-schema instead 390 + '@humanwhocodes/retry@0.3.1': 391 + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} 392 + engines: {node: '>=18.18'} 306 393 307 - '@moonlight-mod/lunast@1.0.0': 308 - resolution: {integrity: sha512-kJgf41K12i6/2LbXK97CNO+pNO7ADGh9N4bCQcOPwosocKMcwKHDEZUgPqeihNshY3c3AEW1LiyXjlsl24PdDw==} 394 + '@humanwhocodes/retry@0.4.2': 395 + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} 396 + engines: {node: '>=18.18'} 309 397 310 - '@moonlight-mod/mappings@1.0.2': 311 - resolution: {integrity: sha512-PjIv4LFyt3j4LyGiokUmJ6a0L5JljoLXjUkixCynLLpNLd660qTcLe8f9tbhOovvD8joqejq+f5oqSo2V4/Vfg==} 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 312 401 peerDependencies: 313 - '@moonlight-mod/lunast': ^1.0.0 314 - '@moonlight-mod/moonmap': ^1.0.0 402 + eslint: '>= 9' 403 + typescript: '>= 5.3' 404 + 405 + '@moonlight-mod/lunast@1.0.1': 406 + resolution: {integrity: sha512-K3vxzDlfFuYKjciIW2FMlcZ1qrrkAGDGpSBlNqYGtJ0sMt9bRCd2lpSpg6AX/giSljDtmAUXa/5mOfUoDQxjBA==} 315 407 316 - '@moonlight-mod/moonmap@1.0.2': 317 - resolution: {integrity: sha512-dqMFwk8o0duRfvBNYo6EwalEUWWR3bNF5V2N04ogHp4gYON6/5+XOUrTlQ9BFBDj9anZwGgVwGqnxV42Qs9pPw==} 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==} 318 417 319 418 '@nodelib/fs.scandir@2.1.5': 320 419 resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} ··· 328 427 resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 329 428 engines: {node: '>= 8'} 330 429 331 - '@pkgr/utils@2.4.2': 332 - resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} 430 + '@pkgr/core@0.2.0': 431 + resolution: {integrity: sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==} 333 432 engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} 334 433 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==} 440 + 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 + 335 447 '@types/estree-jsx@1.0.5': 336 448 resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} 337 449 338 450 '@types/estree@1.0.6': 339 451 resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 340 452 453 + '@types/estree@1.0.7': 454 + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} 455 + 341 456 '@types/fbemitter@2.0.35': 342 457 resolution: {integrity: sha512-Xem6d7qUfmouCHntCrRYgDBwbf+WWRd6G+7WEFlEZFZ67LZXiYRvT2LV8wcZa6mIaAil95+ABQdKgB6hPIsnng==} 343 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 + 344 465 '@types/flux@3.1.14': 345 466 resolution: {integrity: sha512-WRXN0kQPCnqxN0/PgNgc7WBF6c8rbSHsEep3/qBLpsQ824RONdOmTs0TV7XhIW2GDNRAHO2CqCgAFLR5PChosw==} 346 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==} 473 + 347 474 '@types/json-schema@7.0.15': 348 475 resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 349 476 477 + '@types/lodash@4.17.14': 478 + resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==} 479 + 350 480 '@types/node@18.17.17': 351 481 resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==} 352 482 353 - '@types/node@20.16.10': 354 - resolution: {integrity: sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==} 483 + '@types/node@22.13.6': 484 + resolution: {integrity: sha512-GYmF65GI7417CpZXsEXMjT8goQQDnpRnJnDw6jIYa+le3V/lMazPZ4vZmK1B/9R17fh2VLr2zuy9d/h5xgrLAg==} 485 + 486 + '@types/node@22.14.0': 487 + resolution: {integrity: sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==} 488 + 489 + '@types/platform@1.3.6': 490 + resolution: {integrity: sha512-ZmSaqHuvzv+jC232cFoz2QqPUkaj6EvMmCrWcx3WRr7xTPVFCMUOTcOq8m2d+Zw1iKRc1kDiaA+jtNrV0hkVew==} 355 491 356 492 '@types/prop-types@15.7.13': 357 493 resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==} 358 494 359 - '@types/react@18.3.10': 360 - resolution: {integrity: sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg==} 495 + '@types/react@18.3.20': 496 + resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==} 361 497 362 - '@types/readable-stream@4.0.15': 363 - resolution: {integrity: sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==} 364 - 365 - '@types/semver@7.5.6': 366 - resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} 367 - 368 - '@typescript-eslint/eslint-plugin@6.13.2': 369 - resolution: {integrity: sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==} 370 - 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} 371 501 peerDependencies: 372 - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha 373 - eslint: ^7.0.0 || ^8.0.0 374 - typescript: '*' 375 - peerDependenciesMeta: 376 - typescript: 377 - 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' 378 505 379 - '@typescript-eslint/parser@6.13.2': 380 - resolution: {integrity: sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==} 381 - 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} 382 509 peerDependencies: 383 - eslint: ^7.0.0 || ^8.0.0 384 - typescript: '*' 385 - peerDependenciesMeta: 386 - typescript: 387 - optional: true 510 + eslint: ^8.57.0 || ^9.0.0 511 + typescript: '>=4.8.4 <5.9.0' 388 512 389 - '@typescript-eslint/scope-manager@6.13.2': 390 - resolution: {integrity: sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==} 391 - 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} 392 516 393 - '@typescript-eslint/type-utils@6.13.2': 394 - resolution: {integrity: sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==} 395 - 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} 396 520 peerDependencies: 397 - eslint: ^7.0.0 || ^8.0.0 398 - typescript: '*' 399 - peerDependenciesMeta: 400 - typescript: 401 - optional: true 521 + eslint: ^8.57.0 || ^9.0.0 522 + typescript: '>=4.8.4 <5.9.0' 402 523 403 - '@typescript-eslint/types@6.13.2': 404 - resolution: {integrity: sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==} 405 - 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} 406 527 407 - '@typescript-eslint/typescript-estree@6.13.2': 408 - resolution: {integrity: sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==} 409 - 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} 410 531 peerDependencies: 411 - typescript: '*' 412 - peerDependenciesMeta: 413 - typescript: 414 - optional: true 532 + typescript: '>=4.8.4 <5.9.0' 415 533 416 - '@typescript-eslint/utils@6.13.2': 417 - resolution: {integrity: sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==} 418 - 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} 419 537 peerDependencies: 420 - eslint: ^7.0.0 || ^8.0.0 538 + eslint: ^8.57.0 || ^9.0.0 539 + typescript: '>=4.8.4 <5.9.0' 421 540 422 - '@typescript-eslint/visitor-keys@6.13.2': 423 - resolution: {integrity: sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==} 424 - engines: {node: ^16.0.0 || >=18.0.0} 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} 425 544 426 - '@ungap/structured-clone@1.2.0': 427 - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} 545 + '@xterm/xterm@5.5.0': 546 + resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==} 428 547 429 - '@zenfs/core@1.0.2': 430 - resolution: {integrity: sha512-LMTD4ntn6Ag1y+IeOSVykDDvYC12dsGFtsX8M/54OQrLs7v+YnX4bpo0o2osbm8XFmU2MTNMX/G3PLsvzgWzrg==} 431 - engines: {node: '>= 16'} 548 + '@zenfs/core@2.0.0': 549 + resolution: {integrity: sha512-wOKNFTY1DJ1vdLqKdU7M8cRh0nVYZcDVu7WHuk/3u49hrSwTZVm4PzGxJUjFd8O9Wi3U5nYTbZoN7RX5mS2ldA==} 550 + engines: {node: '>= 18'} 432 551 hasBin: true 433 552 434 - '@zenfs/dom@0.2.16': 435 - resolution: {integrity: sha512-6Ev+ol9hZIgQECNZR+xxjQ/a99EhhrWeiQttm/+U7YJK3HdTjiKfU39DsfGeH64vSqhpa5Vj+LWRx75SHkjw0Q==} 553 + '@zenfs/dom@1.1.6': 554 + resolution: {integrity: sha512-7SBTWgA0esuEv/TE+N/xk6W/XJf8uBF+LhlPNHQdXds0H7aOy/UYsWv/8glvARe+meDMMidoeWFLzUWoMXfjlA==} 436 555 engines: {node: '>= 18'} 437 556 peerDependencies: 438 - '@zenfs/core': ^1.0.0 557 + '@zenfs/core': ^2.0.0 558 + utilium: ^1.9.0 439 559 440 560 abort-controller@3.0.0: 441 561 resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} ··· 446 566 peerDependencies: 447 567 acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 448 568 449 - acorn@8.12.1: 450 - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} 569 + acorn@8.14.1: 570 + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} 451 571 engines: {node: '>=0.4.0'} 452 572 hasBin: true 453 573 454 574 ajv@6.12.6: 455 575 resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 456 576 457 - ansi-regex@5.0.1: 458 - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 459 - engines: {node: '>=8'} 460 - 461 577 ansi-styles@4.3.0: 462 578 resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 463 579 engines: {node: '>=8'} 464 580 581 + ansis@3.17.0: 582 + resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==} 583 + engines: {node: '>=14'} 584 + 465 585 argparse@2.0.1: 466 586 resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 467 587 468 - array-buffer-byte-length@1.0.0: 469 - 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'} 470 591 471 - array-includes@3.1.7: 472 - resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} 592 + array-includes@3.1.8: 593 + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} 473 594 engines: {node: '>= 0.4'} 474 595 475 - array-union@2.1.0: 476 - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 477 - engines: {node: '>=8'} 596 + array.prototype.findlast@1.2.5: 597 + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} 598 + engines: {node: '>= 0.4'} 478 599 479 - array.prototype.flat@1.3.2: 480 - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} 600 + array.prototype.flat@1.3.3: 601 + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} 481 602 engines: {node: '>= 0.4'} 482 603 483 - array.prototype.flatmap@1.3.2: 484 - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} 604 + array.prototype.flatmap@1.3.3: 605 + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} 485 606 engines: {node: '>= 0.4'} 486 607 487 - array.prototype.tosorted@1.1.2: 488 - 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'} 489 611 490 - arraybuffer.prototype.slice@1.0.2: 491 - resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} 612 + arraybuffer.prototype.slice@1.0.4: 613 + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} 492 614 engines: {node: '>= 0.4'} 493 615 494 616 astring@1.9.0: 495 617 resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} 496 618 hasBin: true 497 619 498 - asynciterator.prototype@1.0.0: 499 - resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} 620 + async-function@1.0.0: 621 + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} 622 + engines: {node: '>= 0.4'} 500 623 501 - available-typed-arrays@1.0.5: 502 - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} 624 + available-typed-arrays@1.0.7: 625 + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} 503 626 engines: {node: '>= 0.4'} 504 627 505 628 balanced-match@1.0.2: ··· 508 631 base64-js@1.5.1: 509 632 resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} 510 633 511 - big-integer@1.6.52: 512 - resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} 513 - engines: {node: '>=0.6'} 514 - 515 - bplist-parser@0.2.0: 516 - resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} 517 - engines: {node: '>= 5.10.0'} 518 - 519 634 brace-expansion@1.1.11: 520 635 resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 521 636 522 637 brace-expansion@2.0.1: 523 638 resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 524 639 525 - braces@3.0.2: 526 - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 640 + braces@3.0.3: 641 + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 527 642 engines: {node: '>=8'} 528 643 529 644 buffer@6.0.3: 530 645 resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} 531 646 532 - bundle-name@3.0.0: 533 - resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} 534 - engines: {node: '>=12'} 647 + cac@6.7.14: 648 + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 649 + engines: {node: '>=8'} 535 650 536 - call-bind@1.0.5: 537 - resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} 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'} 538 662 539 663 callsites@3.1.0: 540 664 resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} ··· 554 678 concat-map@0.0.1: 555 679 resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 556 680 557 - cross-spawn@7.0.3: 558 - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 681 + cross-spawn@7.0.6: 682 + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 559 683 engines: {node: '>= 8'} 560 684 561 - csstype@3.1.2: 562 - resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} 563 - 564 685 csstype@3.1.3: 565 686 resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 566 687 567 - debug@4.3.4: 568 - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 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'} 699 + 700 + debug@4.4.0: 701 + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 569 702 engines: {node: '>=6.0'} 570 703 peerDependencies: 571 704 supports-color: '*' ··· 576 709 deep-is@0.1.4: 577 710 resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 578 711 579 - default-browser-id@3.0.0: 580 - resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} 581 - engines: {node: '>=12'} 582 - 583 - default-browser@4.0.0: 584 - resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} 585 - engines: {node: '>=14.16'} 586 - 587 - define-data-property@1.1.1: 588 - 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==} 589 714 engines: {node: '>= 0.4'} 590 715 591 - define-lazy-prop@3.0.0: 592 - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} 593 - engines: {node: '>=12'} 594 - 595 716 define-properties@1.2.1: 596 717 resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} 597 718 engines: {node: '>= 0.4'} 598 719 599 - dir-glob@3.0.1: 600 - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} 601 - 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==} 602 725 603 726 doctrine@2.1.0: 604 727 resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} 605 728 engines: {node: '>=0.10.0'} 606 729 607 - doctrine@3.0.0: 608 - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} 609 - engines: {node: '>=6.0.0'} 730 + dunder-proto@1.0.1: 731 + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 732 + engines: {node: '>= 0.4'} 610 733 611 - es-abstract@1.22.3: 612 - resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} 734 + es-abstract@1.23.9: 735 + resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} 613 736 engines: {node: '>= 0.4'} 614 737 615 - es-iterator-helpers@1.0.15: 616 - resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} 738 + es-define-property@1.0.1: 739 + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 740 + engines: {node: '>= 0.4'} 617 741 618 - es-set-tostringtag@2.0.2: 619 - resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} 742 + es-errors@1.3.0: 743 + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 620 744 engines: {node: '>= 0.4'} 621 745 622 - es-shim-unscopables@1.0.2: 623 - 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'} 624 749 625 - es-to-primitive@1.2.1: 626 - 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==} 627 764 engines: {node: '>= 0.4'} 628 765 629 766 esbuild-copy-static-files@0.1.0: ··· 644 781 peerDependencies: 645 782 eslint: '>=7.0.0' 646 783 647 - eslint-plugin-prettier@5.0.1: 648 - resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} 784 + eslint-plugin-prettier@5.2.6: 785 + resolution: {integrity: sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==} 649 786 engines: {node: ^14.18.0 || >=16.0.0} 650 787 peerDependencies: 651 788 '@types/eslint': '>=8.0.0' 652 789 eslint: '>=8.0.0' 653 - eslint-config-prettier: '*' 790 + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' 654 791 prettier: '>=3.0.0' 655 792 peerDependenciesMeta: 656 793 '@types/eslint': ··· 658 795 eslint-config-prettier: 659 796 optional: true 660 797 661 - eslint-plugin-react@7.33.2: 662 - resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} 798 + eslint-plugin-react@7.37.5: 799 + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} 663 800 engines: {node: '>=4'} 664 801 peerDependencies: 665 - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 802 + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 666 803 667 - eslint-scope@7.2.2: 668 - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} 669 - 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} 670 807 671 808 eslint-visitor-keys@3.4.3: 672 809 resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 673 810 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 674 811 675 - eslint@8.55.0: 676 - resolution: {integrity: sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==} 677 - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 678 - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. 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} 679 819 hasBin: true 820 + peerDependencies: 821 + jiti: '*' 822 + peerDependenciesMeta: 823 + jiti: 824 + optional: true 680 825 681 - espree@9.6.1: 682 - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} 683 - 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} 684 829 685 - esquery@1.5.0: 686 - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} 830 + esquery@1.6.0: 831 + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 687 832 engines: {node: '>=0.10'} 688 833 689 834 esrecurse@4.3.0: ··· 712 857 resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} 713 858 engines: {node: '>=0.8.x'} 714 859 715 - execa@5.1.1: 716 - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} 717 - engines: {node: '>=10'} 718 - 719 - execa@7.2.0: 720 - resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} 721 - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} 722 - 723 860 fast-deep-equal@3.1.3: 724 861 resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 725 862 ··· 736 873 fast-levenshtein@2.0.6: 737 874 resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 738 875 739 - fastq@1.15.0: 740 - resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} 876 + fastq@1.17.1: 877 + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} 741 878 742 - file-entry-cache@6.0.1: 743 - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} 744 - 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 886 + 887 + file-entry-cache@8.0.0: 888 + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 889 + engines: {node: '>=16.0.0'} 745 890 746 - fill-range@7.0.1: 747 - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 891 + fill-range@7.1.1: 892 + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 748 893 engines: {node: '>=8'} 749 894 895 + find-up-simple@1.0.1: 896 + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} 897 + engines: {node: '>=18'} 898 + 750 899 find-up@5.0.0: 751 900 resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 752 901 engines: {node: '>=10'} 753 902 754 - flat-cache@3.2.0: 755 - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} 756 - engines: {node: ^10.12.0 || >=12.0.0} 903 + flat-cache@4.0.1: 904 + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 905 + engines: {node: '>=16'} 757 906 758 907 flatted@3.2.9: 759 908 resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} 760 909 761 - for-each@0.3.3: 762 - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} 763 - 764 - fs.realpath@1.0.0: 765 - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 910 + for-each@0.3.5: 911 + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} 912 + engines: {node: '>= 0.4'} 766 913 767 914 function-bind@1.1.2: 768 915 resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 769 916 770 - function.prototype.name@1.1.6: 771 - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} 917 + function.prototype.name@1.1.8: 918 + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} 772 919 engines: {node: '>= 0.4'} 773 920 774 921 functions-have-names@1.2.3: 775 922 resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} 776 923 777 - get-intrinsic@1.2.2: 778 - 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'} 779 930 780 - get-stream@6.0.1: 781 - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} 782 - engines: {node: '>=10'} 931 + get-proto@1.0.1: 932 + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 933 + engines: {node: '>= 0.4'} 783 934 784 - get-symbol-description@1.0.0: 785 - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} 935 + get-symbol-description@1.1.0: 936 + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} 786 937 engines: {node: '>= 0.4'} 787 938 788 939 glob-parent@5.1.2: ··· 793 944 resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 794 945 engines: {node: '>=10.13.0'} 795 946 796 - glob@7.2.3: 797 - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} 798 - deprecated: Glob versions prior to v9 are no longer supported 947 + globals@14.0.0: 948 + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 949 + engines: {node: '>=18'} 799 950 800 - globals@13.23.0: 801 - resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} 802 - engines: {node: '>=8'} 803 - 804 - globalthis@1.0.3: 805 - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} 951 + globalthis@1.0.4: 952 + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} 806 953 engines: {node: '>= 0.4'} 807 954 808 - globby@11.1.0: 809 - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 810 - engines: {node: '>=10'} 811 - 812 - gopd@1.0.1: 813 - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} 955 + gopd@1.2.0: 956 + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 957 + engines: {node: '>= 0.4'} 814 958 815 959 graphemer@1.4.0: 816 960 resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 817 961 818 - has-bigints@1.0.2: 819 - 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'} 820 965 821 966 has-flag@4.0.0: 822 967 resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 823 968 engines: {node: '>=8'} 824 969 825 - has-property-descriptors@1.0.1: 826 - resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} 970 + has-property-descriptors@1.0.2: 971 + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} 827 972 828 - has-proto@1.0.1: 829 - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} 973 + has-proto@1.2.0: 974 + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} 830 975 engines: {node: '>= 0.4'} 831 976 832 - has-symbols@1.0.3: 833 - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 977 + has-symbols@1.1.0: 978 + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 834 979 engines: {node: '>= 0.4'} 835 980 836 - has-tostringtag@1.0.0: 837 - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} 981 + has-tostringtag@1.0.2: 982 + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} 838 983 engines: {node: '>= 0.4'} 839 984 840 - hasown@2.0.0: 841 - resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} 985 + hasown@2.0.2: 986 + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 842 987 engines: {node: '>= 0.4'} 843 988 844 - human-signals@2.1.0: 845 - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} 846 - engines: {node: '>=10.17.0'} 847 - 848 - human-signals@4.3.1: 849 - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} 850 - engines: {node: '>=14.18.0'} 851 - 852 989 husky@8.0.3: 853 990 resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} 854 991 engines: {node: '>=14'} ··· 857 994 ieee754@1.2.1: 858 995 resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} 859 996 860 - ignore@5.3.0: 861 - resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} 997 + ignore@5.3.2: 998 + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 862 999 engines: {node: '>= 4'} 863 1000 864 1001 import-fresh@3.3.0: ··· 869 1006 resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 870 1007 engines: {node: '>=0.8.19'} 871 1008 872 - inflight@1.0.6: 873 - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 874 - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. 875 - 876 - inherits@2.0.4: 877 - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 878 - 879 - internal-slot@1.0.6: 880 - resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} 1009 + internal-slot@1.1.0: 1010 + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} 881 1011 engines: {node: '>= 0.4'} 882 1012 883 - is-array-buffer@3.0.2: 884 - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} 1013 + is-array-buffer@3.0.5: 1014 + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} 1015 + engines: {node: '>= 0.4'} 885 1016 886 - is-async-function@2.0.0: 887 - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} 1017 + is-async-function@2.1.1: 1018 + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} 888 1019 engines: {node: '>= 0.4'} 889 1020 890 - is-bigint@1.0.4: 891 - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} 1021 + is-bigint@1.1.0: 1022 + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} 1023 + engines: {node: '>= 0.4'} 892 1024 893 - is-boolean-object@1.1.2: 894 - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} 1025 + is-boolean-object@1.2.2: 1026 + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} 895 1027 engines: {node: '>= 0.4'} 896 1028 897 1029 is-callable@1.2.7: 898 1030 resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} 899 1031 engines: {node: '>= 0.4'} 900 1032 901 - is-core-module@2.13.1: 902 - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} 1033 + is-core-module@2.16.1: 1034 + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} 1035 + engines: {node: '>= 0.4'} 903 1036 904 - is-date-object@1.0.5: 905 - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} 1037 + is-data-view@1.0.2: 1038 + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} 906 1039 engines: {node: '>= 0.4'} 907 1040 908 - is-docker@2.2.1: 909 - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} 910 - engines: {node: '>=8'} 911 - hasBin: true 912 - 913 - is-docker@3.0.0: 914 - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} 915 - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 916 - hasBin: true 1041 + is-date-object@1.1.0: 1042 + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} 1043 + engines: {node: '>= 0.4'} 917 1044 918 1045 is-extglob@2.1.1: 919 1046 resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 920 1047 engines: {node: '>=0.10.0'} 921 1048 922 - is-finalizationregistry@1.0.2: 923 - 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'} 924 1052 925 - is-generator-function@1.0.10: 926 - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} 1053 + is-generator-function@1.1.0: 1054 + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} 927 1055 engines: {node: '>= 0.4'} 928 1056 929 1057 is-glob@4.0.3: 930 1058 resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 931 1059 engines: {node: '>=0.10.0'} 932 1060 933 - is-inside-container@1.0.0: 934 - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} 935 - engines: {node: '>=14.16'} 936 - hasBin: true 937 - 938 - is-map@2.0.2: 939 - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} 940 - 941 - is-negative-zero@2.0.2: 942 - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} 1061 + is-map@2.0.3: 1062 + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} 943 1063 engines: {node: '>= 0.4'} 944 1064 945 - is-number-object@1.0.7: 946 - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} 1065 + is-number-object@1.1.1: 1066 + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} 947 1067 engines: {node: '>= 0.4'} 948 1068 949 1069 is-number@7.0.0: 950 1070 resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 951 1071 engines: {node: '>=0.12.0'} 952 1072 953 - is-path-inside@3.0.3: 954 - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} 955 - engines: {node: '>=8'} 1073 + is-regex@1.2.1: 1074 + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} 1075 + engines: {node: '>= 0.4'} 956 1076 957 - is-regex@1.1.4: 958 - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} 1077 + is-set@2.0.3: 1078 + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} 959 1079 engines: {node: '>= 0.4'} 960 1080 961 - is-set@2.0.2: 962 - 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'} 963 1084 964 - is-shared-array-buffer@1.0.2: 965 - 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'} 966 1088 967 - is-stream@2.0.1: 968 - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} 969 - engines: {node: '>=8'} 1089 + is-symbol@1.1.1: 1090 + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} 1091 + engines: {node: '>= 0.4'} 970 1092 971 - is-stream@3.0.0: 972 - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} 973 - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 1093 + is-typed-array@1.1.15: 1094 + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} 1095 + engines: {node: '>= 0.4'} 974 1096 975 - is-string@1.0.7: 976 - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} 1097 + is-weakmap@2.0.2: 1098 + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} 977 1099 engines: {node: '>= 0.4'} 978 1100 979 - is-symbol@1.0.4: 980 - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} 1101 + is-weakref@1.1.1: 1102 + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} 981 1103 engines: {node: '>= 0.4'} 982 1104 983 - is-typed-array@1.1.12: 984 - resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} 1105 + is-weakset@2.0.4: 1106 + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} 985 1107 engines: {node: '>= 0.4'} 986 1108 987 - is-weakmap@2.0.1: 988 - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} 989 - 990 - is-weakref@1.0.2: 991 - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} 992 - 993 - is-weakset@2.0.2: 994 - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} 995 - 996 - is-wsl@2.2.0: 997 - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} 998 - engines: {node: '>=8'} 999 - 1000 1109 isarray@2.0.5: 1001 1110 resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} 1002 1111 1003 1112 isexe@2.0.0: 1004 1113 resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1005 1114 1006 - iterator.prototype@1.1.2: 1007 - 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 1008 1122 1009 1123 js-tokens@4.0.0: 1010 1124 resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} ··· 1044 1158 resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 1045 1159 hasBin: true 1046 1160 1047 - lru-cache@6.0.0: 1048 - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 1049 - engines: {node: '>=10'} 1050 - 1051 - merge-stream@2.0.0: 1052 - 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'} 1053 1164 1054 1165 merge2@1.4.1: 1055 1166 resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} ··· 1059 1170 resolution: {integrity: sha512-OyvYIOgpzXREySYJ1cqEb2pOKdeQMTfF9M8dRU6nC4hi/GXMmNpe9ssZCrSoTHazu05BSAoRBN/uYeco+ymfOg==} 1060 1171 engines: {node: '>=18.0.0'} 1061 1172 1062 - micromatch@4.0.5: 1063 - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} 1064 - engines: {node: '>=8.6'} 1173 + microdiff@1.5.0: 1174 + resolution: {integrity: sha512-Drq+/THMvDdzRYrK0oxJmOKiC24ayUV8ahrt8l3oRK51PWt6gdtrIGrlIH3pT/lFh1z93FbAcidtsHcWbnRz8Q==} 1065 1175 1066 - mimic-fn@2.1.0: 1067 - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} 1068 - engines: {node: '>=6'} 1176 + micromatch@4.0.8: 1177 + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 1178 + engines: {node: '>=8.6'} 1069 1179 1070 - mimic-fn@4.0.0: 1071 - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} 1072 - engines: {node: '>=12'} 1180 + mimic-function@5.0.1: 1181 + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} 1182 + engines: {node: '>=18'} 1073 1183 1074 1184 minimatch@3.1.2: 1075 1185 resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} ··· 1078 1188 resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 1079 1189 engines: {node: '>=16 || 14 >=14.17'} 1080 1190 1081 - ms@2.1.2: 1082 - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 1191 + ms@2.1.3: 1192 + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1083 1193 1084 1194 nanotar@0.1.1: 1085 1195 resolution: {integrity: sha512-AiJsGsSF3O0havL1BydvI4+wR76sKT+okKRwWIaK96cZUnXqH0uNBOsHlbwZq3+m2BR1VKqHDVudl3gO4mYjpQ==} ··· 1087 1197 natural-compare@1.4.0: 1088 1198 resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 1089 1199 1090 - npm-run-path@4.0.1: 1091 - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} 1092 - engines: {node: '>=8'} 1093 - 1094 - npm-run-path@5.1.0: 1095 - resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} 1096 - 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==} 1097 1202 1098 1203 object-assign@4.1.1: 1099 1204 resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 1100 1205 engines: {node: '>=0.10.0'} 1101 1206 1102 - object-inspect@1.13.1: 1103 - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} 1207 + object-inspect@1.13.4: 1208 + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} 1209 + engines: {node: '>= 0.4'} 1104 1210 1105 1211 object-keys@1.1.1: 1106 1212 resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} 1107 1213 engines: {node: '>= 0.4'} 1108 1214 1109 - object.assign@4.1.5: 1110 - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} 1215 + object.assign@4.1.7: 1216 + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} 1111 1217 engines: {node: '>= 0.4'} 1112 1218 1113 - object.entries@1.1.7: 1114 - resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} 1219 + object.entries@1.1.9: 1220 + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} 1115 1221 engines: {node: '>= 0.4'} 1116 1222 1117 - object.fromentries@2.0.7: 1118 - resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} 1223 + object.fromentries@2.0.8: 1224 + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} 1119 1225 engines: {node: '>= 0.4'} 1120 1226 1121 - object.hasown@1.1.3: 1122 - resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} 1123 - 1124 - object.values@1.1.7: 1125 - resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} 1227 + object.values@1.2.1: 1228 + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} 1126 1229 engines: {node: '>= 0.4'} 1127 1230 1128 - once@1.4.0: 1129 - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 1231 + ofetch@1.4.1: 1232 + resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} 1130 1233 1131 - onetime@5.1.2: 1132 - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} 1133 - engines: {node: '>=6'} 1134 - 1135 - onetime@6.0.0: 1136 - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} 1137 - engines: {node: '>=12'} 1138 - 1139 - open@9.1.0: 1140 - resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} 1141 - engines: {node: '>=14.16'} 1234 + onetime@7.0.0: 1235 + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} 1236 + engines: {node: '>=18'} 1142 1237 1143 1238 optionator@0.9.3: 1144 1239 resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} 1145 1240 engines: {node: '>= 0.8.0'} 1146 1241 1242 + own-keys@1.0.1: 1243 + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} 1244 + engines: {node: '>= 0.4'} 1245 + 1147 1246 p-limit@3.1.0: 1148 1247 resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 1149 1248 engines: {node: '>=10'} ··· 1152 1251 resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 1153 1252 engines: {node: '>=10'} 1154 1253 1254 + package-manager-detector@1.1.0: 1255 + resolution: {integrity: sha512-Y8f9qUlBzW8qauJjd/eu6jlpJZsuPJm2ZAV0cDVd420o4EdpH5RPdoCv+60/TdJflGatr4sDfpAL6ArWZbM5tA==} 1256 + 1155 1257 parent-module@1.0.1: 1156 1258 resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 1157 1259 engines: {node: '>=6'} ··· 1159 1261 path-exists@4.0.0: 1160 1262 resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1161 1263 engines: {node: '>=8'} 1162 - 1163 - path-is-absolute@1.0.1: 1164 - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 1165 - engines: {node: '>=0.10.0'} 1166 1264 1167 1265 path-key@3.1.1: 1168 1266 resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1169 1267 engines: {node: '>=8'} 1170 1268 1171 - path-key@4.0.0: 1172 - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} 1173 - engines: {node: '>=12'} 1174 - 1175 1269 path-parse@1.0.7: 1176 1270 resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1177 1271 1178 - path-type@4.0.0: 1179 - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 1180 - engines: {node: '>=8'} 1181 - 1182 - picocolors@1.0.0: 1183 - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 1272 + pathe@2.0.3: 1273 + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} 1184 1274 1185 1275 picomatch@2.3.1: 1186 1276 resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1187 1277 engines: {node: '>=8.6'} 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'} 1188 1289 1189 1290 prelude-ls@1.2.1: 1190 1291 resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} ··· 1209 1310 punycode@2.3.1: 1210 1311 resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 1211 1312 engines: {node: '>=6'} 1313 + 1314 + quansync@0.2.10: 1315 + resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} 1212 1316 1213 1317 queue-microtask@1.2.3: 1214 1318 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} ··· 1220 1324 resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} 1221 1325 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 1222 1326 1223 - reflect.getprototypeof@1.0.4: 1224 - resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} 1327 + reflect.getprototypeof@1.0.10: 1328 + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} 1225 1329 engines: {node: '>= 0.4'} 1226 1330 1227 - regexp.prototype.flags@1.5.1: 1228 - resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} 1331 + regexp.prototype.flags@1.5.4: 1332 + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} 1229 1333 engines: {node: '>= 0.4'} 1230 1334 1231 1335 resolve-from@4.0.0: ··· 1236 1340 resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} 1237 1341 hasBin: true 1238 1342 1343 + restore-cursor@5.1.0: 1344 + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} 1345 + engines: {node: '>=18'} 1346 + 1239 1347 reusify@1.0.4: 1240 1348 resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 1241 1349 engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1242 - 1243 - rimraf@3.0.2: 1244 - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 1245 - deprecated: Rimraf versions prior to v4 are no longer supported 1246 - hasBin: true 1247 - 1248 - run-applescript@5.0.0: 1249 - resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} 1250 - engines: {node: '>=12'} 1251 1350 1252 1351 run-parallel@1.2.0: 1253 1352 resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1254 1353 1255 - safe-array-concat@1.0.1: 1256 - resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} 1354 + safe-array-concat@1.1.3: 1355 + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} 1257 1356 engines: {node: '>=0.4'} 1258 1357 1259 - safe-buffer@5.1.2: 1260 - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 1261 - 1262 1358 safe-buffer@5.2.1: 1263 1359 resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 1264 1360 1265 - safe-regex-test@1.0.0: 1266 - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} 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'} 1267 1368 1268 1369 semver@6.3.1: 1269 1370 resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 1270 1371 hasBin: true 1271 1372 1272 - semver@7.5.4: 1273 - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} 1373 + semver@7.7.1: 1374 + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} 1274 1375 engines: {node: '>=10'} 1275 1376 hasBin: true 1276 1377 1277 - set-function-length@1.1.1: 1278 - resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} 1378 + set-function-length@1.2.2: 1379 + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} 1380 + engines: {node: '>= 0.4'} 1381 + 1382 + set-function-name@2.0.2: 1383 + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} 1279 1384 engines: {node: '>= 0.4'} 1280 1385 1281 - set-function-name@2.0.1: 1282 - resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} 1386 + set-proto@1.0.0: 1387 + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} 1283 1388 engines: {node: '>= 0.4'} 1284 1389 1285 1390 shebang-command@2.0.0: ··· 1290 1395 resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1291 1396 engines: {node: '>=8'} 1292 1397 1293 - side-channel@1.0.4: 1294 - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} 1398 + side-channel-list@1.0.0: 1399 + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} 1400 + engines: {node: '>= 0.4'} 1295 1401 1296 - signal-exit@3.0.7: 1297 - 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'} 1298 1405 1299 - slash@3.0.0: 1300 - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 1301 - 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'} 1302 1417 1303 1418 standalone-electron-types@1.0.0: 1304 1419 resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==} 1305 1420 1306 - string.prototype.matchall@4.0.10: 1307 - resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} 1421 + string.prototype.matchall@4.0.12: 1422 + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} 1423 + engines: {node: '>= 0.4'} 1308 1424 1309 - string.prototype.trim@1.2.8: 1310 - resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} 1425 + string.prototype.repeat@1.0.0: 1426 + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} 1427 + 1428 + string.prototype.trim@1.2.10: 1429 + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} 1311 1430 engines: {node: '>= 0.4'} 1312 1431 1313 - string.prototype.trimend@1.0.7: 1314 - resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} 1432 + string.prototype.trimend@1.0.9: 1433 + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} 1434 + engines: {node: '>= 0.4'} 1315 1435 1316 - string.prototype.trimstart@1.0.7: 1317 - resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} 1436 + string.prototype.trimstart@1.0.8: 1437 + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} 1438 + engines: {node: '>= 0.4'} 1318 1439 1319 1440 string_decoder@1.3.0: 1320 1441 resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} 1321 1442 1322 - strip-ansi@6.0.1: 1323 - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 1324 - engines: {node: '>=8'} 1325 - 1326 - strip-final-newline@2.0.0: 1327 - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} 1328 - engines: {node: '>=6'} 1329 - 1330 - strip-final-newline@3.0.0: 1331 - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} 1332 - engines: {node: '>=12'} 1333 - 1334 1443 strip-json-comments@3.1.1: 1335 1444 resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 1336 1445 engines: {node: '>=8'} ··· 1343 1452 resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 1344 1453 engines: {node: '>= 0.4'} 1345 1454 1346 - synckit@0.8.6: 1347 - resolution: {integrity: sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==} 1455 + synckit@0.11.1: 1456 + resolution: {integrity: sha512-fWZqNBZNNFp/7mTUy1fSsydhKsAKJ+u90Nk7kOK5Gcq9vObaqLBLjWFDBkyVU9Vvc6Y71VbOevMuGhqv02bT+Q==} 1348 1457 engines: {node: ^14.18.0 || >=16.0.0} 1349 1458 1350 - text-table@0.2.0: 1351 - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} 1459 + taze@19.0.4: 1460 + resolution: {integrity: sha512-bviyNotzqcIWpVBCC4QYVb2yupzKyUDGQi2m/8GERdiPaudVMtgAqaE98+x0cDDaByYRMJCyhQWM04ikUL6+kQ==} 1461 + hasBin: true 1352 1462 1353 - titleize@3.0.0: 1354 - resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} 1355 - 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'} 1356 1469 1357 1470 to-regex-range@5.0.1: 1358 1471 resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1359 1472 engines: {node: '>=8.0'} 1360 1473 1361 - ts-api-utils@1.0.3: 1362 - resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} 1363 - 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'} 1364 1477 peerDependencies: 1365 - typescript: '>=4.2.0' 1478 + typescript: '>=4.8.4' 1366 1479 1367 - tslib@2.6.2: 1368 - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} 1480 + tslib@2.8.1: 1481 + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1369 1482 1370 1483 type-check@0.4.0: 1371 1484 resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 1372 1485 engines: {node: '>= 0.8.0'} 1373 1486 1374 - type-fest@0.20.2: 1375 - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} 1376 - engines: {node: '>=10'} 1487 + typed-array-buffer@1.0.3: 1488 + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} 1489 + engines: {node: '>= 0.4'} 1377 1490 1378 - typed-array-buffer@1.0.0: 1379 - resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} 1491 + typed-array-byte-length@1.0.3: 1492 + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} 1380 1493 engines: {node: '>= 0.4'} 1381 1494 1382 - typed-array-byte-length@1.0.0: 1383 - resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} 1495 + typed-array-byte-offset@1.0.4: 1496 + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} 1384 1497 engines: {node: '>= 0.4'} 1385 1498 1386 - typed-array-byte-offset@1.0.0: 1387 - resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} 1499 + typed-array-length@1.0.7: 1500 + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} 1388 1501 engines: {node: '>= 0.4'} 1389 1502 1390 - typed-array-length@1.0.4: 1391 - 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' 1392 1509 1393 - typescript@5.3.2: 1394 - resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==} 1510 + typescript@5.8.2: 1511 + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} 1395 1512 engines: {node: '>=14.17'} 1396 1513 hasBin: true 1397 1514 1398 - unbox-primitive@1.0.2: 1399 - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} 1515 + ufo@1.5.4: 1516 + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} 1517 + 1518 + unbox-primitive@1.1.0: 1519 + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} 1520 + engines: {node: '>= 0.4'} 1400 1521 1401 - undici-types@6.19.8: 1402 - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 1522 + unconfig@7.3.1: 1523 + resolution: {integrity: sha512-LH5WL+un92tGAzWS87k7LkAfwpMdm7V0IXG2FxEjZz/QxiIW5J5LkcrKQThj0aRz6+h/lFmKI9EUXmK/T0bcrw==} 1403 1524 1404 - untildify@4.0.0: 1405 - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} 1406 - engines: {node: '>=8'} 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==} 1407 1530 1408 1531 uri-js@4.4.1: 1409 1532 resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 1410 1533 1411 - utilium@0.7.1: 1412 - resolution: {integrity: sha512-2ocvTkI7U8LERmwxL0LhFUvEfN66UqcjF6tMiURvUwSyU7U1QC9gST+3iSUSiGccFfnP3f2EXwHNXOnOzx+lAg==} 1534 + utilium@1.10.1: 1535 + resolution: {integrity: sha512-GQINDTb/ocyz4acQj3GXAe0wipYxws6L+9ouqaq10KlInTk9DGvW9TJd0pYa/Xu3cppNnZuB4T/sBuSXpcN2ng==} 1413 1536 1414 - which-boxed-primitive@1.0.2: 1415 - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} 1537 + which-boxed-primitive@1.1.1: 1538 + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} 1539 + engines: {node: '>= 0.4'} 1416 1540 1417 - which-builtin-type@1.1.3: 1418 - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} 1541 + which-builtin-type@1.2.1: 1542 + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} 1419 1543 engines: {node: '>= 0.4'} 1420 1544 1421 - which-collection@1.0.1: 1422 - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} 1545 + which-collection@1.0.2: 1546 + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} 1547 + engines: {node: '>= 0.4'} 1423 1548 1424 - which-typed-array@1.1.13: 1425 - resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} 1549 + which-typed-array@1.1.19: 1550 + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} 1426 1551 engines: {node: '>= 0.4'} 1427 1552 1428 1553 which@2.0.2: ··· 1430 1555 engines: {node: '>= 8'} 1431 1556 hasBin: true 1432 1557 1433 - wrappy@1.0.2: 1434 - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 1435 - 1436 - yallist@4.0.0: 1437 - 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 1438 1562 1439 1563 yocto-queue@0.1.0: 1440 1564 resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1441 1565 engines: {node: '>=10'} 1442 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 + 1443 1585 snapshots: 1444 1586 1445 1587 '@aashutoshrathi/word-wrap@1.2.6': {} 1588 + 1589 + '@antfu/ni@24.3.0': 1590 + dependencies: 1591 + ansis: 3.17.0 1592 + fzf: 0.5.2 1593 + package-manager-detector: 1.1.0 1594 + tinyexec: 1.0.1 1446 1595 1447 1596 '@esbuild/android-arm64@0.19.3': 1448 1597 optional: true ··· 1510 1659 '@esbuild/win32-x64@0.19.3': 1511 1660 optional: true 1512 1661 1513 - '@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))': 1514 1663 dependencies: 1515 - eslint: 8.55.0 1664 + eslint: 9.23.0(jiti@2.4.2) 1516 1665 eslint-visitor-keys: 3.4.3 1517 1666 1518 - '@eslint-community/regexpp@4.10.0': {} 1667 + '@eslint-community/regexpp@4.12.1': {} 1519 1668 1520 - '@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': 1521 1688 dependencies: 1522 1689 ajv: 6.12.6 1523 - debug: 4.3.4 1524 - espree: 9.6.1 1525 - globals: 13.23.0 1526 - ignore: 5.3.0 1690 + debug: 4.4.0 1691 + espree: 10.3.0 1692 + globals: 14.0.0 1693 + ignore: 5.3.2 1527 1694 import-fresh: 3.3.0 1528 1695 js-yaml: 4.1.0 1529 1696 minimatch: 3.1.2 ··· 1531 1698 transitivePeerDependencies: 1532 1699 - supports-color 1533 1700 1534 - '@eslint/js@8.55.0': {} 1701 + '@eslint/js@9.23.0': {} 1702 + 1703 + '@eslint/object-schema@2.1.6': {} 1535 1704 1536 - '@humanwhocodes/config-array@0.11.13': 1705 + '@eslint/plugin-kit@0.2.8': 1537 1706 dependencies: 1538 - '@humanwhocodes/object-schema': 2.0.1 1539 - debug: 4.3.4 1540 - minimatch: 3.1.2 1541 - transitivePeerDependencies: 1542 - - supports-color 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 1543 1716 1544 1717 '@humanwhocodes/module-importer@1.0.1': {} 1545 1718 1546 - '@humanwhocodes/object-schema@2.0.1': {} 1719 + '@humanwhocodes/retry@0.3.1': {} 1720 + 1721 + '@humanwhocodes/retry@0.4.2': {} 1547 1722 1548 - '@moonlight-mod/lunast@1.0.0': 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) 1732 + transitivePeerDependencies: 1733 + - '@types/eslint' 1734 + - prettier 1735 + - supports-color 1736 + 1737 + '@moonlight-mod/lunast@1.0.1': 1549 1738 dependencies: 1550 1739 astring: 1.9.0 1551 1740 estree-toolkit: 1.7.8 1552 1741 meriyah: 6.0.1 1553 1742 1554 - '@moonlight-mod/mappings@1.0.2(@moonlight-mod/lunast@1.0.0)(@moonlight-mod/moonmap@1.0.2)': 1743 + '@moonlight-mod/mappings@1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)': 1555 1744 dependencies: 1556 - '@moonlight-mod/lunast': 1.0.0 1557 - '@moonlight-mod/moonmap': 1.0.2 1745 + '@moonlight-mod/lunast': 1.0.1 1746 + '@moonlight-mod/moonmap': 1.0.5 1747 + '@types/chroma-js': 3.1.0 1558 1748 '@types/flux': 3.1.14 1559 - '@types/react': 18.3.10 1749 + '@types/highlightjs': 9.12.6 1750 + '@types/lodash': 4.17.14 1751 + '@types/platform': 1.3.6 1752 + '@types/react': 18.3.20 1560 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 1561 1759 1562 - '@moonlight-mod/moonmap@1.0.2': {} 1760 + '@moonlight-mod/moonmap@1.0.5': {} 1563 1761 1564 1762 '@nodelib/fs.scandir@2.1.5': 1565 1763 dependencies: ··· 1571 1769 '@nodelib/fs.walk@1.2.8': 1572 1770 dependencies: 1573 1771 '@nodelib/fs.scandir': 2.1.5 1574 - fastq: 1.15.0 1772 + fastq: 1.17.1 1773 + 1774 + '@pkgr/core@0.2.0': {} 1575 1775 1576 - '@pkgr/utils@2.4.2': 1776 + '@quansync/fs@0.1.2': 1577 1777 dependencies: 1578 - cross-spawn: 7.0.3 1579 - fast-glob: 3.3.2 1580 - is-glob: 4.0.3 1581 - open: 9.1.0 1582 - picocolors: 1.0.0 1583 - 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 1584 1792 1585 1793 '@types/estree-jsx@1.0.5': 1586 1794 dependencies: ··· 1588 1796 1589 1797 '@types/estree@1.0.6': {} 1590 1798 1799 + '@types/estree@1.0.7': 1800 + optional: true 1801 + 1591 1802 '@types/fbemitter@2.0.35': {} 1592 1803 1804 + '@types/filesystem@0.0.36': 1805 + dependencies: 1806 + '@types/filewriter': 0.0.33 1807 + 1808 + '@types/filewriter@0.0.33': {} 1809 + 1593 1810 '@types/flux@3.1.14': 1594 1811 dependencies: 1595 1812 '@types/fbemitter': 2.0.35 1596 - '@types/react': 18.3.10 1813 + '@types/react': 18.3.20 1814 + 1815 + '@types/har-format@1.2.16': {} 1816 + 1817 + '@types/highlightjs@9.12.6': {} 1597 1818 1598 1819 '@types/json-schema@7.0.15': {} 1599 1820 1821 + '@types/lodash@4.17.14': {} 1822 + 1600 1823 '@types/node@18.17.17': {} 1601 1824 1602 - '@types/node@20.16.10': 1825 + '@types/node@22.13.6': 1826 + dependencies: 1827 + undici-types: 6.20.0 1828 + 1829 + '@types/node@22.14.0': 1603 1830 dependencies: 1604 - undici-types: 6.19.8 1831 + undici-types: 6.21.0 1832 + 1833 + '@types/platform@1.3.6': {} 1605 1834 1606 1835 '@types/prop-types@15.7.13': {} 1607 1836 1608 - '@types/react@18.3.10': 1837 + '@types/react@18.3.20': 1609 1838 dependencies: 1610 1839 '@types/prop-types': 15.7.13 1611 1840 csstype: 3.1.3 1612 1841 1613 - '@types/readable-stream@4.0.15': 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)': 1614 1843 dependencies: 1615 - '@types/node': 20.16.10 1616 - safe-buffer: 5.1.2 1617 - 1618 - '@types/semver@7.5.6': {} 1619 - 1620 - '@typescript-eslint/eslint-plugin@6.13.2(@typescript-eslint/parser@6.13.2(eslint@8.55.0)(typescript@5.3.2))(eslint@8.55.0)(typescript@5.3.2)': 1621 - dependencies: 1622 - '@eslint-community/regexpp': 4.10.0 1623 - '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.2) 1624 - '@typescript-eslint/scope-manager': 6.13.2 1625 - '@typescript-eslint/type-utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2) 1626 - '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2) 1627 - '@typescript-eslint/visitor-keys': 6.13.2 1628 - debug: 4.3.4 1629 - eslint: 8.55.0 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) 1630 1851 graphemer: 1.4.0 1631 - ignore: 5.3.0 1852 + ignore: 5.3.2 1632 1853 natural-compare: 1.4.0 1633 - semver: 7.5.4 1634 - ts-api-utils: 1.0.3(typescript@5.3.2) 1635 - optionalDependencies: 1636 - typescript: 5.3.2 1854 + ts-api-utils: 2.1.0(typescript@5.8.2) 1855 + typescript: 5.8.2 1637 1856 transitivePeerDependencies: 1638 1857 - supports-color 1639 1858 1640 - '@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)': 1641 1860 dependencies: 1642 - '@typescript-eslint/scope-manager': 6.13.2 1643 - '@typescript-eslint/types': 6.13.2 1644 - '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2) 1645 - '@typescript-eslint/visitor-keys': 6.13.2 1646 - debug: 4.3.4 1647 - eslint: 8.55.0 1648 - optionalDependencies: 1649 - 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 1650 1868 transitivePeerDependencies: 1651 1869 - supports-color 1652 1870 1653 - '@typescript-eslint/scope-manager@6.13.2': 1871 + '@typescript-eslint/scope-manager@8.29.0': 1654 1872 dependencies: 1655 - '@typescript-eslint/types': 6.13.2 1656 - '@typescript-eslint/visitor-keys': 6.13.2 1873 + '@typescript-eslint/types': 8.29.0 1874 + '@typescript-eslint/visitor-keys': 8.29.0 1657 1875 1658 - '@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)': 1659 1877 dependencies: 1660 - '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2) 1661 - '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2) 1662 - debug: 4.3.4 1663 - eslint: 8.55.0 1664 - ts-api-utils: 1.0.3(typescript@5.3.2) 1665 - optionalDependencies: 1666 - 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 1667 1884 transitivePeerDependencies: 1668 1885 - supports-color 1669 1886 1670 - '@typescript-eslint/types@6.13.2': {} 1887 + '@typescript-eslint/types@8.29.0': {} 1671 1888 1672 - '@typescript-eslint/typescript-estree@6.13.2(typescript@5.3.2)': 1889 + '@typescript-eslint/typescript-estree@8.29.0(typescript@5.8.2)': 1673 1890 dependencies: 1674 - '@typescript-eslint/types': 6.13.2 1675 - '@typescript-eslint/visitor-keys': 6.13.2 1676 - debug: 4.3.4 1677 - 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 1678 1895 is-glob: 4.0.3 1679 - semver: 7.5.4 1680 - ts-api-utils: 1.0.3(typescript@5.3.2) 1681 - optionalDependencies: 1682 - 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 1683 1900 transitivePeerDependencies: 1684 1901 - supports-color 1685 1902 1686 - '@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)': 1687 1904 dependencies: 1688 - '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0) 1689 - '@types/json-schema': 7.0.15 1690 - '@types/semver': 7.5.6 1691 - '@typescript-eslint/scope-manager': 6.13.2 1692 - '@typescript-eslint/types': 6.13.2 1693 - '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2) 1694 - eslint: 8.55.0 1695 - 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 1696 1911 transitivePeerDependencies: 1697 1912 - supports-color 1698 - - typescript 1699 1913 1700 - '@typescript-eslint/visitor-keys@6.13.2': 1914 + '@typescript-eslint/visitor-keys@8.29.0': 1701 1915 dependencies: 1702 - '@typescript-eslint/types': 6.13.2 1703 - eslint-visitor-keys: 3.4.3 1916 + '@typescript-eslint/types': 8.29.0 1917 + eslint-visitor-keys: 4.2.0 1704 1918 1705 - '@ungap/structured-clone@1.2.0': {} 1919 + '@xterm/xterm@5.5.0': 1920 + optional: true 1706 1921 1707 - '@zenfs/core@1.0.2': 1922 + '@zenfs/core@2.0.0': 1708 1923 dependencies: 1709 - '@types/node': 20.16.10 1710 - '@types/readable-stream': 4.0.15 1924 + '@types/node': 22.13.6 1711 1925 buffer: 6.0.3 1712 1926 eventemitter3: 5.0.1 1713 - minimatch: 9.0.5 1714 1927 readable-stream: 4.5.2 1715 - utilium: 0.7.1 1928 + utilium: 1.10.1 1716 1929 1717 - '@zenfs/dom@0.2.16(@zenfs/core@1.0.2)': 1930 + '@zenfs/dom@1.1.6(@zenfs/core@2.0.0)(utilium@1.10.1)': 1718 1931 dependencies: 1719 - '@zenfs/core': 1.0.2 1932 + '@zenfs/core': 2.0.0 1933 + utilium: 1.10.1 1720 1934 1721 1935 abort-controller@3.0.0: 1722 1936 dependencies: 1723 1937 event-target-shim: 5.0.1 1724 1938 1725 - acorn-jsx@5.3.2(acorn@8.12.1): 1939 + acorn-jsx@5.3.2(acorn@8.14.1): 1726 1940 dependencies: 1727 - acorn: 8.12.1 1941 + acorn: 8.14.1 1728 1942 1729 - acorn@8.12.1: {} 1943 + acorn@8.14.1: {} 1730 1944 1731 1945 ajv@6.12.6: 1732 1946 dependencies: ··· 1734 1948 fast-json-stable-stringify: 2.1.0 1735 1949 json-schema-traverse: 0.4.1 1736 1950 uri-js: 4.4.1 1737 - 1738 - ansi-regex@5.0.1: {} 1739 1951 1740 1952 ansi-styles@4.3.0: 1741 1953 dependencies: 1742 1954 color-convert: 2.0.1 1743 1955 1956 + ansis@3.17.0: {} 1957 + 1744 1958 argparse@2.0.1: {} 1745 1959 1746 - array-buffer-byte-length@1.0.0: 1960 + array-buffer-byte-length@1.0.2: 1747 1961 dependencies: 1748 - call-bind: 1.0.5 1749 - is-array-buffer: 3.0.2 1962 + call-bound: 1.0.4 1963 + is-array-buffer: 3.0.5 1750 1964 1751 - array-includes@3.1.7: 1965 + array-includes@3.1.8: 1752 1966 dependencies: 1753 - call-bind: 1.0.5 1967 + call-bind: 1.0.8 1754 1968 define-properties: 1.2.1 1755 - es-abstract: 1.22.3 1756 - get-intrinsic: 1.2.2 1757 - is-string: 1.0.7 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 1758 1973 1759 - array-union@2.1.0: {} 1974 + array.prototype.findlast@1.2.5: 1975 + dependencies: 1976 + call-bind: 1.0.8 1977 + define-properties: 1.2.1 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 1760 1982 1761 - array.prototype.flat@1.3.2: 1983 + array.prototype.flat@1.3.3: 1762 1984 dependencies: 1763 - call-bind: 1.0.5 1985 + call-bind: 1.0.8 1764 1986 define-properties: 1.2.1 1765 - es-abstract: 1.22.3 1766 - es-shim-unscopables: 1.0.2 1987 + es-abstract: 1.23.9 1988 + es-shim-unscopables: 1.1.0 1767 1989 1768 - array.prototype.flatmap@1.3.2: 1990 + array.prototype.flatmap@1.3.3: 1769 1991 dependencies: 1770 - call-bind: 1.0.5 1992 + call-bind: 1.0.8 1771 1993 define-properties: 1.2.1 1772 - es-abstract: 1.22.3 1773 - es-shim-unscopables: 1.0.2 1994 + es-abstract: 1.23.9 1995 + es-shim-unscopables: 1.1.0 1774 1996 1775 - array.prototype.tosorted@1.1.2: 1997 + array.prototype.tosorted@1.1.4: 1776 1998 dependencies: 1777 - call-bind: 1.0.5 1999 + call-bind: 1.0.8 1778 2000 define-properties: 1.2.1 1779 - es-abstract: 1.22.3 1780 - es-shim-unscopables: 1.0.2 1781 - get-intrinsic: 1.2.2 2001 + es-abstract: 1.23.9 2002 + es-errors: 1.3.0 2003 + es-shim-unscopables: 1.1.0 1782 2004 1783 - arraybuffer.prototype.slice@1.0.2: 2005 + arraybuffer.prototype.slice@1.0.4: 1784 2006 dependencies: 1785 - array-buffer-byte-length: 1.0.0 1786 - call-bind: 1.0.5 2007 + array-buffer-byte-length: 1.0.2 2008 + call-bind: 1.0.8 1787 2009 define-properties: 1.2.1 1788 - es-abstract: 1.22.3 1789 - get-intrinsic: 1.2.2 1790 - is-array-buffer: 3.0.2 1791 - is-shared-array-buffer: 1.0.2 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 1792 2014 1793 2015 astring@1.9.0: {} 1794 2016 1795 - asynciterator.prototype@1.0.0: 1796 - dependencies: 1797 - has-symbols: 1.0.3 2017 + async-function@1.0.0: {} 1798 2018 1799 - available-typed-arrays@1.0.5: {} 2019 + available-typed-arrays@1.0.7: 2020 + dependencies: 2021 + possible-typed-array-names: 1.1.0 1800 2022 1801 2023 balanced-match@1.0.2: {} 1802 2024 1803 2025 base64-js@1.5.1: {} 1804 2026 1805 - big-integer@1.6.52: {} 1806 - 1807 - bplist-parser@0.2.0: 1808 - dependencies: 1809 - big-integer: 1.6.52 1810 - 1811 2027 brace-expansion@1.1.11: 1812 2028 dependencies: 1813 2029 balanced-match: 1.0.2 ··· 1817 2033 dependencies: 1818 2034 balanced-match: 1.0.2 1819 2035 1820 - braces@3.0.2: 2036 + braces@3.0.3: 1821 2037 dependencies: 1822 - fill-range: 7.0.1 2038 + fill-range: 7.1.1 1823 2039 1824 2040 buffer@6.0.3: 1825 2041 dependencies: 1826 2042 base64-js: 1.5.1 1827 2043 ieee754: 1.2.1 1828 2044 1829 - bundle-name@3.0.0: 1830 - dependencies: 1831 - run-applescript: 5.0.0 2045 + cac@6.7.14: {} 1832 2046 1833 - call-bind@1.0.5: 2047 + call-bind-apply-helpers@1.0.2: 1834 2048 dependencies: 2049 + es-errors: 1.3.0 1835 2050 function-bind: 1.1.2 1836 - get-intrinsic: 1.2.2 1837 - 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 1838 2063 1839 2064 callsites@3.1.0: {} 1840 2065 ··· 1851 2076 1852 2077 concat-map@0.0.1: {} 1853 2078 1854 - cross-spawn@7.0.3: 2079 + cross-spawn@7.0.6: 1855 2080 dependencies: 1856 2081 path-key: 3.1.1 1857 2082 shebang-command: 2.0.0 1858 2083 which: 2.0.2 1859 2084 1860 - csstype@3.1.2: {} 1861 - 1862 2085 csstype@3.1.3: {} 1863 2086 1864 - debug@4.3.4: 2087 + data-view-buffer@1.0.2: 1865 2088 dependencies: 1866 - ms: 2.1.2 1867 - 1868 - deep-is@0.1.4: {} 2089 + call-bound: 1.0.4 2090 + es-errors: 1.3.0 2091 + is-data-view: 1.0.2 1869 2092 1870 - default-browser-id@3.0.0: 2093 + data-view-byte-length@1.0.2: 1871 2094 dependencies: 1872 - bplist-parser: 0.2.0 1873 - untildify: 4.0.0 2095 + call-bound: 1.0.4 2096 + es-errors: 1.3.0 2097 + is-data-view: 1.0.2 1874 2098 1875 - default-browser@4.0.0: 2099 + data-view-byte-offset@1.0.1: 1876 2100 dependencies: 1877 - bundle-name: 3.0.0 1878 - default-browser-id: 3.0.0 1879 - execa: 7.2.0 1880 - titleize: 3.0.0 2101 + call-bound: 1.0.4 2102 + es-errors: 1.3.0 2103 + is-data-view: 1.0.2 1881 2104 1882 - define-data-property@1.1.1: 2105 + debug@4.4.0: 1883 2106 dependencies: 1884 - get-intrinsic: 1.2.2 1885 - gopd: 1.0.1 1886 - has-property-descriptors: 1.0.1 2107 + ms: 2.1.3 1887 2108 1888 - 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 1889 2116 1890 2117 define-properties@1.2.1: 1891 2118 dependencies: 1892 - define-data-property: 1.1.1 1893 - has-property-descriptors: 1.0.1 2119 + define-data-property: 1.1.4 2120 + has-property-descriptors: 1.0.2 1894 2121 object-keys: 1.1.1 1895 2122 1896 - dir-glob@3.0.1: 1897 - dependencies: 1898 - path-type: 4.0.0 2123 + defu@6.1.4: {} 2124 + 2125 + destr@2.0.4: {} 1899 2126 1900 2127 doctrine@2.1.0: 1901 2128 dependencies: 1902 2129 esutils: 2.0.3 1903 2130 1904 - doctrine@3.0.0: 2131 + dunder-proto@1.0.1: 1905 2132 dependencies: 1906 - esutils: 2.0.3 2133 + call-bind-apply-helpers: 1.0.2 2134 + es-errors: 1.3.0 2135 + gopd: 1.2.0 1907 2136 1908 - es-abstract@1.22.3: 2137 + es-abstract@1.23.9: 1909 2138 dependencies: 1910 - array-buffer-byte-length: 1.0.0 1911 - arraybuffer.prototype.slice: 1.0.2 1912 - available-typed-arrays: 1.0.5 1913 - call-bind: 1.0.5 1914 - es-set-tostringtag: 2.0.2 1915 - es-to-primitive: 1.2.1 1916 - function.prototype.name: 1.1.6 1917 - get-intrinsic: 1.2.2 1918 - get-symbol-description: 1.0.0 1919 - globalthis: 1.0.3 1920 - gopd: 1.0.1 1921 - has-property-descriptors: 1.0.1 1922 - has-proto: 1.0.1 1923 - has-symbols: 1.0.3 1924 - hasown: 2.0.0 1925 - internal-slot: 1.0.6 1926 - 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 1927 2164 is-callable: 1.2.7 1928 - is-negative-zero: 2.0.2 1929 - is-regex: 1.1.4 1930 - is-shared-array-buffer: 1.0.2 1931 - is-string: 1.0.7 1932 - is-typed-array: 1.1.12 1933 - is-weakref: 1.0.2 1934 - 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 1935 2173 object-keys: 1.1.1 1936 - object.assign: 4.1.5 1937 - regexp.prototype.flags: 1.5.1 1938 - safe-array-concat: 1.0.1 1939 - safe-regex-test: 1.0.0 1940 - string.prototype.trim: 1.2.8 1941 - string.prototype.trimend: 1.0.7 1942 - string.prototype.trimstart: 1.0.7 1943 - typed-array-buffer: 1.0.0 1944 - typed-array-byte-length: 1.0.0 1945 - typed-array-byte-offset: 1.0.0 1946 - typed-array-length: 1.0.4 1947 - unbox-primitive: 1.0.2 1948 - 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 2190 + 2191 + es-define-property@1.0.1: {} 2192 + 2193 + es-errors@1.3.0: {} 1949 2194 1950 - es-iterator-helpers@1.0.15: 2195 + es-iterator-helpers@1.2.1: 1951 2196 dependencies: 1952 - asynciterator.prototype: 1.0.0 1953 - call-bind: 1.0.5 2197 + call-bind: 1.0.8 2198 + call-bound: 1.0.4 1954 2199 define-properties: 1.2.1 1955 - es-abstract: 1.22.3 1956 - 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 1957 2203 function-bind: 1.1.2 1958 - get-intrinsic: 1.2.2 1959 - globalthis: 1.0.3 1960 - has-property-descriptors: 1.0.1 1961 - has-proto: 1.0.1 1962 - has-symbols: 1.0.3 1963 - internal-slot: 1.0.6 1964 - iterator.prototype: 1.1.2 1965 - 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 1966 2213 1967 - es-set-tostringtag@2.0.2: 2214 + es-object-atoms@1.1.1: 1968 2215 dependencies: 1969 - get-intrinsic: 1.2.2 1970 - has-tostringtag: 1.0.0 1971 - hasown: 2.0.0 2216 + es-errors: 1.3.0 1972 2217 1973 - es-shim-unscopables@1.0.2: 2218 + es-set-tostringtag@2.1.0: 2219 + dependencies: 2220 + es-errors: 1.3.0 2221 + get-intrinsic: 1.3.0 2222 + has-tostringtag: 1.0.2 2223 + hasown: 2.0.2 2224 + 2225 + es-shim-unscopables@1.1.0: 1974 2226 dependencies: 1975 - hasown: 2.0.0 2227 + hasown: 2.0.2 1976 2228 1977 - es-to-primitive@1.2.1: 2229 + es-to-primitive@1.3.0: 1978 2230 dependencies: 1979 2231 is-callable: 1.2.7 1980 - is-date-object: 1.0.5 1981 - is-symbol: 1.0.4 2232 + is-date-object: 1.1.0 2233 + is-symbol: 1.1.1 1982 2234 1983 2235 esbuild-copy-static-files@0.1.0: {} 1984 2236 ··· 2009 2261 2010 2262 escape-string-regexp@4.0.0: {} 2011 2263 2012 - 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)): 2013 2265 dependencies: 2014 - eslint: 8.55.0 2266 + eslint: 9.23.0(jiti@2.4.2) 2015 2267 2016 - eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.1.0(eslint@8.55.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): 2017 2269 dependencies: 2018 - eslint: 8.55.0 2270 + eslint: 9.23.0(jiti@2.4.2) 2019 2271 prettier: 3.1.0 2020 2272 prettier-linter-helpers: 1.0.0 2021 - synckit: 0.8.6 2273 + synckit: 0.11.1 2022 2274 optionalDependencies: 2023 - eslint-config-prettier: 9.1.0(eslint@8.55.0) 2275 + '@types/eslint': 9.6.1 2276 + eslint-config-prettier: 9.1.0(eslint@9.23.0(jiti@2.4.2)) 2024 2277 2025 - 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)): 2026 2279 dependencies: 2027 - array-includes: 3.1.7 2028 - array.prototype.flatmap: 1.3.2 2029 - 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 2030 2284 doctrine: 2.1.0 2031 - es-iterator-helpers: 1.0.15 2032 - eslint: 8.55.0 2285 + es-iterator-helpers: 1.2.1 2286 + eslint: 9.23.0(jiti@2.4.2) 2033 2287 estraverse: 5.3.0 2288 + hasown: 2.0.2 2034 2289 jsx-ast-utils: 3.3.5 2035 2290 minimatch: 3.1.2 2036 - object.entries: 1.1.7 2037 - object.fromentries: 2.0.7 2038 - object.hasown: 1.1.3 2039 - object.values: 1.1.7 2291 + object.entries: 1.1.9 2292 + object.fromentries: 2.0.8 2293 + object.values: 1.2.1 2040 2294 prop-types: 15.8.1 2041 2295 resolve: 2.0.0-next.5 2042 2296 semver: 6.3.1 2043 - string.prototype.matchall: 4.0.10 2297 + string.prototype.matchall: 4.0.12 2298 + string.prototype.repeat: 1.0.0 2044 2299 2045 - eslint-scope@7.2.2: 2300 + eslint-scope@8.3.0: 2046 2301 dependencies: 2047 2302 esrecurse: 4.3.0 2048 2303 estraverse: 5.3.0 2049 2304 2050 2305 eslint-visitor-keys@3.4.3: {} 2051 2306 2052 - eslint@8.55.0: 2307 + eslint-visitor-keys@4.2.0: {} 2308 + 2309 + eslint@9.23.0(jiti@2.4.2): 2053 2310 dependencies: 2054 - '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0) 2055 - '@eslint-community/regexpp': 4.10.0 2056 - '@eslint/eslintrc': 2.1.4 2057 - '@eslint/js': 8.55.0 2058 - '@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 2059 2320 '@humanwhocodes/module-importer': 1.0.1 2060 - '@nodelib/fs.walk': 1.2.8 2061 - '@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 2062 2324 ajv: 6.12.6 2063 2325 chalk: 4.1.2 2064 - cross-spawn: 7.0.3 2065 - debug: 4.3.4 2066 - doctrine: 3.0.0 2326 + cross-spawn: 7.0.6 2327 + debug: 4.4.0 2067 2328 escape-string-regexp: 4.0.0 2068 - eslint-scope: 7.2.2 2069 - eslint-visitor-keys: 3.4.3 2070 - espree: 9.6.1 2071 - 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 2072 2333 esutils: 2.0.3 2073 2334 fast-deep-equal: 3.1.3 2074 - file-entry-cache: 6.0.1 2335 + file-entry-cache: 8.0.0 2075 2336 find-up: 5.0.0 2076 2337 glob-parent: 6.0.2 2077 - globals: 13.23.0 2078 - graphemer: 1.4.0 2079 - ignore: 5.3.0 2338 + ignore: 5.3.2 2080 2339 imurmurhash: 0.1.4 2081 2340 is-glob: 4.0.3 2082 - is-path-inside: 3.0.3 2083 - js-yaml: 4.1.0 2084 2341 json-stable-stringify-without-jsonify: 1.0.1 2085 - levn: 0.4.1 2086 2342 lodash.merge: 4.6.2 2087 2343 minimatch: 3.1.2 2088 2344 natural-compare: 1.4.0 2089 2345 optionator: 0.9.3 2090 - strip-ansi: 6.0.1 2091 - text-table: 0.2.0 2346 + optionalDependencies: 2347 + jiti: 2.4.2 2092 2348 transitivePeerDependencies: 2093 2349 - supports-color 2094 2350 2095 - espree@9.6.1: 2351 + espree@10.3.0: 2096 2352 dependencies: 2097 - acorn: 8.12.1 2098 - acorn-jsx: 5.3.2(acorn@8.12.1) 2099 - 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 2100 2356 2101 - esquery@1.5.0: 2357 + esquery@1.6.0: 2102 2358 dependencies: 2103 2359 estraverse: 5.3.0 2104 2360 ··· 2121 2377 2122 2378 events@3.3.0: {} 2123 2379 2124 - execa@5.1.1: 2125 - dependencies: 2126 - cross-spawn: 7.0.3 2127 - get-stream: 6.0.1 2128 - human-signals: 2.1.0 2129 - is-stream: 2.0.1 2130 - merge-stream: 2.0.0 2131 - npm-run-path: 4.0.1 2132 - onetime: 5.1.2 2133 - signal-exit: 3.0.7 2134 - strip-final-newline: 2.0.0 2135 - 2136 - execa@7.2.0: 2137 - dependencies: 2138 - cross-spawn: 7.0.3 2139 - get-stream: 6.0.1 2140 - human-signals: 4.3.1 2141 - is-stream: 3.0.0 2142 - merge-stream: 2.0.0 2143 - npm-run-path: 5.1.0 2144 - onetime: 6.0.0 2145 - signal-exit: 3.0.7 2146 - strip-final-newline: 3.0.0 2147 - 2148 2380 fast-deep-equal@3.1.3: {} 2149 2381 2150 2382 fast-diff@1.3.0: {} ··· 2155 2387 '@nodelib/fs.walk': 1.2.8 2156 2388 glob-parent: 5.1.2 2157 2389 merge2: 1.4.1 2158 - micromatch: 4.0.5 2390 + micromatch: 4.0.8 2159 2391 2160 2392 fast-json-stable-stringify@2.1.0: {} 2161 2393 2162 2394 fast-levenshtein@2.0.6: {} 2163 2395 2164 - fastq@1.15.0: 2396 + fastq@1.17.1: 2165 2397 dependencies: 2166 2398 reusify: 1.0.4 2167 2399 2168 - 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: 2169 2405 dependencies: 2170 - flat-cache: 3.2.0 2406 + flat-cache: 4.0.1 2171 2407 2172 - fill-range@7.0.1: 2408 + fill-range@7.1.1: 2173 2409 dependencies: 2174 2410 to-regex-range: 5.0.1 2411 + 2412 + find-up-simple@1.0.1: {} 2175 2413 2176 2414 find-up@5.0.0: 2177 2415 dependencies: 2178 2416 locate-path: 6.0.0 2179 2417 path-exists: 4.0.0 2180 2418 2181 - flat-cache@3.2.0: 2419 + flat-cache@4.0.1: 2182 2420 dependencies: 2183 2421 flatted: 3.2.9 2184 2422 keyv: 4.5.4 2185 - rimraf: 3.0.2 2186 2423 2187 2424 flatted@3.2.9: {} 2188 2425 2189 - for-each@0.3.3: 2426 + for-each@0.3.5: 2190 2427 dependencies: 2191 2428 is-callable: 1.2.7 2192 2429 2193 - fs.realpath@1.0.0: {} 2194 - 2195 2430 function-bind@1.1.2: {} 2196 2431 2197 - function.prototype.name@1.1.6: 2432 + function.prototype.name@1.1.8: 2198 2433 dependencies: 2199 - call-bind: 1.0.5 2434 + call-bind: 1.0.8 2435 + call-bound: 1.0.4 2200 2436 define-properties: 1.2.1 2201 - es-abstract: 1.22.3 2202 2437 functions-have-names: 1.2.3 2438 + hasown: 2.0.2 2439 + is-callable: 1.2.7 2203 2440 2204 2441 functions-have-names@1.2.3: {} 2205 2442 2206 - get-intrinsic@1.2.2: 2443 + fzf@0.5.2: {} 2444 + 2445 + get-intrinsic@1.3.0: 2207 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 2208 2451 function-bind: 1.1.2 2209 - has-proto: 1.0.1 2210 - has-symbols: 1.0.3 2211 - 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 2212 2457 2213 - 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 2214 2462 2215 - get-symbol-description@1.0.0: 2463 + get-symbol-description@1.1.0: 2216 2464 dependencies: 2217 - call-bind: 1.0.5 2218 - get-intrinsic: 1.2.2 2465 + call-bound: 1.0.4 2466 + es-errors: 1.3.0 2467 + get-intrinsic: 1.3.0 2219 2468 2220 2469 glob-parent@5.1.2: 2221 2470 dependencies: ··· 2225 2474 dependencies: 2226 2475 is-glob: 4.0.3 2227 2476 2228 - glob@7.2.3: 2229 - dependencies: 2230 - fs.realpath: 1.0.0 2231 - inflight: 1.0.6 2232 - inherits: 2.0.4 2233 - minimatch: 3.1.2 2234 - once: 1.4.0 2235 - path-is-absolute: 1.0.1 2477 + globals@14.0.0: {} 2236 2478 2237 - globals@13.23.0: 2238 - dependencies: 2239 - type-fest: 0.20.2 2240 - 2241 - globalthis@1.0.3: 2479 + globalthis@1.0.4: 2242 2480 dependencies: 2243 2481 define-properties: 1.2.1 2244 - 2245 - globby@11.1.0: 2246 - dependencies: 2247 - array-union: 2.1.0 2248 - dir-glob: 3.0.1 2249 - fast-glob: 3.3.2 2250 - ignore: 5.3.0 2251 - merge2: 1.4.1 2252 - slash: 3.0.0 2482 + gopd: 1.2.0 2253 2483 2254 - gopd@1.0.1: 2255 - dependencies: 2256 - get-intrinsic: 1.2.2 2484 + gopd@1.2.0: {} 2257 2485 2258 2486 graphemer@1.4.0: {} 2259 2487 2260 - has-bigints@1.0.2: {} 2488 + has-bigints@1.1.0: {} 2261 2489 2262 2490 has-flag@4.0.0: {} 2263 2491 2264 - has-property-descriptors@1.0.1: 2492 + has-property-descriptors@1.0.2: 2265 2493 dependencies: 2266 - get-intrinsic: 1.2.2 2494 + es-define-property: 1.0.1 2267 2495 2268 - has-proto@1.0.1: {} 2496 + has-proto@1.2.0: 2497 + dependencies: 2498 + dunder-proto: 1.0.1 2269 2499 2270 - has-symbols@1.0.3: {} 2500 + has-symbols@1.1.0: {} 2271 2501 2272 - has-tostringtag@1.0.0: 2502 + has-tostringtag@1.0.2: 2273 2503 dependencies: 2274 - has-symbols: 1.0.3 2504 + has-symbols: 1.1.0 2275 2505 2276 - hasown@2.0.0: 2506 + hasown@2.0.2: 2277 2507 dependencies: 2278 2508 function-bind: 1.1.2 2279 2509 2280 - human-signals@2.1.0: {} 2281 - 2282 - human-signals@4.3.1: {} 2283 - 2284 2510 husky@8.0.3: {} 2285 2511 2286 2512 ieee754@1.2.1: {} 2287 2513 2288 - ignore@5.3.0: {} 2514 + ignore@5.3.2: {} 2289 2515 2290 2516 import-fresh@3.3.0: 2291 2517 dependencies: ··· 2294 2520 2295 2521 imurmurhash@0.1.4: {} 2296 2522 2297 - inflight@1.0.6: 2523 + internal-slot@1.1.0: 2298 2524 dependencies: 2299 - once: 1.4.0 2300 - wrappy: 1.0.2 2525 + es-errors: 1.3.0 2526 + hasown: 2.0.2 2527 + side-channel: 1.1.0 2301 2528 2302 - inherits@2.0.4: {} 2303 - 2304 - internal-slot@1.0.6: 2529 + is-array-buffer@3.0.5: 2305 2530 dependencies: 2306 - get-intrinsic: 1.2.2 2307 - hasown: 2.0.0 2308 - side-channel: 1.0.4 2309 - 2310 - is-array-buffer@3.0.2: 2311 - dependencies: 2312 - call-bind: 1.0.5 2313 - get-intrinsic: 1.2.2 2314 - is-typed-array: 1.1.12 2531 + call-bind: 1.0.8 2532 + call-bound: 1.0.4 2533 + get-intrinsic: 1.3.0 2315 2534 2316 - is-async-function@2.0.0: 2535 + is-async-function@2.1.1: 2317 2536 dependencies: 2318 - has-tostringtag: 1.0.0 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 2319 2542 2320 - is-bigint@1.0.4: 2543 + is-bigint@1.1.0: 2321 2544 dependencies: 2322 - has-bigints: 1.0.2 2545 + has-bigints: 1.1.0 2323 2546 2324 - is-boolean-object@1.1.2: 2547 + is-boolean-object@1.2.2: 2325 2548 dependencies: 2326 - call-bind: 1.0.5 2327 - has-tostringtag: 1.0.0 2549 + call-bound: 1.0.4 2550 + has-tostringtag: 1.0.2 2328 2551 2329 2552 is-callable@1.2.7: {} 2330 2553 2331 - is-core-module@2.13.1: 2554 + is-core-module@2.16.1: 2332 2555 dependencies: 2333 - hasown: 2.0.0 2556 + hasown: 2.0.2 2334 2557 2335 - is-date-object@1.0.5: 2558 + is-data-view@1.0.2: 2336 2559 dependencies: 2337 - has-tostringtag: 1.0.0 2560 + call-bound: 1.0.4 2561 + get-intrinsic: 1.3.0 2562 + is-typed-array: 1.1.15 2338 2563 2339 - is-docker@2.2.1: {} 2340 - 2341 - 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 2342 2568 2343 2569 is-extglob@2.1.1: {} 2344 2570 2345 - is-finalizationregistry@1.0.2: 2571 + is-finalizationregistry@1.1.1: 2346 2572 dependencies: 2347 - call-bind: 1.0.5 2573 + call-bound: 1.0.4 2348 2574 2349 - is-generator-function@1.0.10: 2575 + is-generator-function@1.1.0: 2350 2576 dependencies: 2351 - 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 2352 2581 2353 2582 is-glob@4.0.3: 2354 2583 dependencies: 2355 2584 is-extglob: 2.1.1 2356 2585 2357 - is-inside-container@1.0.0: 2358 - dependencies: 2359 - is-docker: 3.0.0 2586 + is-map@2.0.3: {} 2360 2587 2361 - is-map@2.0.2: {} 2362 - 2363 - is-negative-zero@2.0.2: {} 2364 - 2365 - is-number-object@1.0.7: 2588 + is-number-object@1.1.1: 2366 2589 dependencies: 2367 - has-tostringtag: 1.0.0 2590 + call-bound: 1.0.4 2591 + has-tostringtag: 1.0.2 2368 2592 2369 2593 is-number@7.0.0: {} 2370 2594 2371 - is-path-inside@3.0.3: {} 2372 - 2373 - is-regex@1.1.4: 2595 + is-regex@1.2.1: 2374 2596 dependencies: 2375 - call-bind: 1.0.5 2376 - 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 2377 2601 2378 - is-set@2.0.2: {} 2602 + is-set@2.0.3: {} 2379 2603 2380 - is-shared-array-buffer@1.0.2: 2604 + is-shared-array-buffer@1.0.4: 2381 2605 dependencies: 2382 - call-bind: 1.0.5 2383 - 2384 - is-stream@2.0.1: {} 2385 - 2386 - is-stream@3.0.0: {} 2606 + call-bound: 1.0.4 2387 2607 2388 - is-string@1.0.7: 2608 + is-string@1.1.1: 2389 2609 dependencies: 2390 - has-tostringtag: 1.0.0 2610 + call-bound: 1.0.4 2611 + has-tostringtag: 1.0.2 2391 2612 2392 - is-symbol@1.0.4: 2613 + is-symbol@1.1.1: 2393 2614 dependencies: 2394 - has-symbols: 1.0.3 2615 + call-bound: 1.0.4 2616 + has-symbols: 1.1.0 2617 + safe-regex-test: 1.1.0 2395 2618 2396 - is-typed-array@1.1.12: 2619 + is-typed-array@1.1.15: 2397 2620 dependencies: 2398 - which-typed-array: 1.1.13 2621 + which-typed-array: 1.1.19 2399 2622 2400 - is-weakmap@2.0.1: {} 2623 + is-weakmap@2.0.2: {} 2401 2624 2402 - is-weakref@1.0.2: 2625 + is-weakref@1.1.1: 2403 2626 dependencies: 2404 - call-bind: 1.0.5 2627 + call-bound: 1.0.4 2405 2628 2406 - is-weakset@2.0.2: 2629 + is-weakset@2.0.4: 2407 2630 dependencies: 2408 - call-bind: 1.0.5 2409 - get-intrinsic: 1.2.2 2410 - 2411 - is-wsl@2.2.0: 2412 - dependencies: 2413 - is-docker: 2.2.1 2631 + call-bound: 1.0.4 2632 + get-intrinsic: 1.3.0 2414 2633 2415 2634 isarray@2.0.5: {} 2416 2635 2417 2636 isexe@2.0.0: {} 2418 2637 2419 - iterator.prototype@1.1.2: 2638 + iterator.prototype@1.1.5: 2420 2639 dependencies: 2421 - define-properties: 1.2.1 2422 - get-intrinsic: 1.2.2 2423 - has-symbols: 1.0.3 2424 - reflect.getprototypeof: 1.0.4 2425 - 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: {} 2426 2648 2427 2649 js-tokens@4.0.0: {} 2428 2650 ··· 2438 2660 2439 2661 jsx-ast-utils@3.3.5: 2440 2662 dependencies: 2441 - array-includes: 3.1.7 2442 - array.prototype.flat: 1.3.2 2443 - object.assign: 4.1.5 2444 - 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 2445 2667 2446 2668 keyv@4.5.4: 2447 2669 dependencies: ··· 2462 2684 dependencies: 2463 2685 js-tokens: 4.0.0 2464 2686 2465 - lru-cache@6.0.0: 2466 - dependencies: 2467 - yallist: 4.0.0 2468 - 2469 - merge-stream@2.0.0: {} 2687 + math-intrinsics@1.1.0: {} 2470 2688 2471 2689 merge2@1.4.1: {} 2472 2690 2473 2691 meriyah@6.0.1: {} 2474 2692 2475 - micromatch@4.0.5: 2693 + microdiff@1.5.0: {} 2694 + 2695 + micromatch@4.0.8: 2476 2696 dependencies: 2477 - braces: 3.0.2 2697 + braces: 3.0.3 2478 2698 picomatch: 2.3.1 2479 2699 2480 - mimic-fn@2.1.0: {} 2481 - 2482 - mimic-fn@4.0.0: {} 2700 + mimic-function@5.0.1: {} 2483 2701 2484 2702 minimatch@3.1.2: 2485 2703 dependencies: ··· 2489 2707 dependencies: 2490 2708 brace-expansion: 2.0.1 2491 2709 2492 - ms@2.1.2: {} 2710 + ms@2.1.3: {} 2493 2711 2494 2712 nanotar@0.1.1: {} 2495 2713 2496 2714 natural-compare@1.4.0: {} 2497 2715 2498 - npm-run-path@4.0.1: 2499 - dependencies: 2500 - path-key: 3.1.1 2501 - 2502 - npm-run-path@5.1.0: 2503 - dependencies: 2504 - path-key: 4.0.0 2716 + node-fetch-native@1.6.6: {} 2505 2717 2506 2718 object-assign@4.1.1: {} 2507 2719 2508 - object-inspect@1.13.1: {} 2720 + object-inspect@1.13.4: {} 2509 2721 2510 2722 object-keys@1.1.1: {} 2511 2723 2512 - object.assign@4.1.5: 2724 + object.assign@4.1.7: 2513 2725 dependencies: 2514 - call-bind: 1.0.5 2726 + call-bind: 1.0.8 2727 + call-bound: 1.0.4 2515 2728 define-properties: 1.2.1 2516 - has-symbols: 1.0.3 2729 + es-object-atoms: 1.1.1 2730 + has-symbols: 1.1.0 2517 2731 object-keys: 1.1.1 2518 2732 2519 - object.entries@1.1.7: 2733 + object.entries@1.1.9: 2520 2734 dependencies: 2521 - call-bind: 1.0.5 2735 + call-bind: 1.0.8 2736 + call-bound: 1.0.4 2522 2737 define-properties: 1.2.1 2523 - es-abstract: 1.22.3 2738 + es-object-atoms: 1.1.1 2524 2739 2525 - object.fromentries@2.0.7: 2740 + object.fromentries@2.0.8: 2526 2741 dependencies: 2527 - call-bind: 1.0.5 2528 - define-properties: 1.2.1 2529 - es-abstract: 1.22.3 2530 - 2531 - object.hasown@1.1.3: 2532 - dependencies: 2742 + call-bind: 1.0.8 2533 2743 define-properties: 1.2.1 2534 - es-abstract: 1.22.3 2744 + es-abstract: 1.23.9 2745 + es-object-atoms: 1.1.1 2535 2746 2536 - object.values@1.1.7: 2747 + object.values@1.2.1: 2537 2748 dependencies: 2538 - call-bind: 1.0.5 2749 + call-bind: 1.0.8 2750 + call-bound: 1.0.4 2539 2751 define-properties: 1.2.1 2540 - es-abstract: 1.22.3 2752 + es-object-atoms: 1.1.1 2541 2753 2542 - once@1.4.0: 2754 + ofetch@1.4.1: 2543 2755 dependencies: 2544 - wrappy: 1.0.2 2545 - 2546 - onetime@5.1.2: 2547 - dependencies: 2548 - mimic-fn: 2.1.0 2549 - 2550 - onetime@6.0.0: 2551 - dependencies: 2552 - mimic-fn: 4.0.0 2756 + destr: 2.0.4 2757 + node-fetch-native: 1.6.6 2758 + ufo: 1.5.4 2553 2759 2554 - open@9.1.0: 2760 + onetime@7.0.0: 2555 2761 dependencies: 2556 - default-browser: 4.0.0 2557 - define-lazy-prop: 3.0.0 2558 - is-inside-container: 1.0.0 2559 - is-wsl: 2.2.0 2762 + mimic-function: 5.0.1 2560 2763 2561 2764 optionator@0.9.3: 2562 2765 dependencies: ··· 2567 2770 prelude-ls: 1.2.1 2568 2771 type-check: 0.4.0 2569 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 + 2570 2779 p-limit@3.1.0: 2571 2780 dependencies: 2572 2781 yocto-queue: 0.1.0 ··· 2574 2783 p-locate@5.0.0: 2575 2784 dependencies: 2576 2785 p-limit: 3.1.0 2786 + 2787 + package-manager-detector@1.1.0: {} 2577 2788 2578 2789 parent-module@1.0.1: 2579 2790 dependencies: ··· 2581 2792 2582 2793 path-exists@4.0.0: {} 2583 2794 2584 - path-is-absolute@1.0.1: {} 2585 - 2586 2795 path-key@3.1.1: {} 2587 2796 2588 - path-key@4.0.0: {} 2589 - 2590 2797 path-parse@1.0.7: {} 2591 2798 2592 - path-type@4.0.0: {} 2799 + pathe@2.0.3: {} 2593 2800 2594 - picocolors@1.0.0: {} 2801 + picomatch@2.3.1: {} 2802 + 2803 + picomatch@4.0.2: {} 2804 + 2805 + pnpm-workspace-yaml@0.3.1: 2806 + dependencies: 2807 + yaml: 2.7.1 2595 2808 2596 - picomatch@2.3.1: {} 2809 + possible-typed-array-names@1.1.0: {} 2597 2810 2598 2811 prelude-ls@1.2.1: {} 2599 2812 ··· 2613 2826 2614 2827 punycode@2.3.1: {} 2615 2828 2829 + quansync@0.2.10: {} 2830 + 2616 2831 queue-microtask@1.2.3: {} 2617 2832 2618 2833 react-is@16.13.1: {} ··· 2625 2840 process: 0.11.10 2626 2841 string_decoder: 1.3.0 2627 2842 2628 - reflect.getprototypeof@1.0.4: 2843 + reflect.getprototypeof@1.0.10: 2629 2844 dependencies: 2630 - call-bind: 1.0.5 2845 + call-bind: 1.0.8 2631 2846 define-properties: 1.2.1 2632 - es-abstract: 1.22.3 2633 - get-intrinsic: 1.2.2 2634 - globalthis: 1.0.3 2635 - 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 2636 2853 2637 - regexp.prototype.flags@1.5.1: 2854 + regexp.prototype.flags@1.5.4: 2638 2855 dependencies: 2639 - call-bind: 1.0.5 2856 + call-bind: 1.0.8 2640 2857 define-properties: 1.2.1 2641 - 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 2642 2862 2643 2863 resolve-from@4.0.0: {} 2644 2864 2645 2865 resolve@2.0.0-next.5: 2646 2866 dependencies: 2647 - is-core-module: 2.13.1 2867 + is-core-module: 2.16.1 2648 2868 path-parse: 1.0.7 2649 2869 supports-preserve-symlinks-flag: 1.0.0 2650 2870 2651 - reusify@1.0.4: {} 2652 - 2653 - rimraf@3.0.2: 2871 + restore-cursor@5.1.0: 2654 2872 dependencies: 2655 - glob: 7.2.3 2873 + onetime: 7.0.0 2874 + signal-exit: 4.1.0 2656 2875 2657 - run-applescript@5.0.0: 2658 - dependencies: 2659 - execa: 5.1.1 2876 + reusify@1.0.4: {} 2660 2877 2661 2878 run-parallel@1.2.0: 2662 2879 dependencies: 2663 2880 queue-microtask: 1.2.3 2664 2881 2665 - safe-array-concat@1.0.1: 2882 + safe-array-concat@1.1.3: 2666 2883 dependencies: 2667 - call-bind: 1.0.5 2668 - get-intrinsic: 1.2.2 2669 - 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 2670 2888 isarray: 2.0.5 2671 2889 2672 - safe-buffer@5.1.2: {} 2673 - 2674 2890 safe-buffer@5.2.1: {} 2675 2891 2676 - safe-regex-test@1.0.0: 2892 + safe-push-apply@1.0.0: 2677 2893 dependencies: 2678 - call-bind: 1.0.5 2679 - get-intrinsic: 1.2.2 2680 - 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 2681 2902 2682 2903 semver@6.3.1: {} 2683 2904 2684 - semver@7.5.4: 2685 - dependencies: 2686 - lru-cache: 6.0.0 2905 + semver@7.7.1: {} 2687 2906 2688 - set-function-length@1.1.1: 2907 + set-function-length@1.2.2: 2689 2908 dependencies: 2690 - define-data-property: 1.1.1 2691 - get-intrinsic: 1.2.2 2692 - gopd: 1.0.1 2693 - 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 2694 2915 2695 - set-function-name@2.0.1: 2916 + set-function-name@2.0.2: 2696 2917 dependencies: 2697 - define-data-property: 1.1.1 2918 + define-data-property: 1.1.4 2919 + es-errors: 1.3.0 2698 2920 functions-have-names: 1.2.3 2699 - 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 2700 2928 2701 2929 shebang-command@2.0.0: 2702 2930 dependencies: ··· 2704 2932 2705 2933 shebang-regex@3.0.0: {} 2706 2934 2707 - side-channel@1.0.4: 2935 + side-channel-list@1.0.0: 2708 2936 dependencies: 2709 - call-bind: 1.0.5 2710 - get-intrinsic: 1.2.2 2711 - object-inspect: 1.13.1 2937 + es-errors: 1.3.0 2938 + object-inspect: 1.13.4 2712 2939 2713 - 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 2714 2946 2715 - 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: {} 2716 2964 2717 2965 standalone-electron-types@1.0.0: 2718 2966 dependencies: 2719 2967 '@types/node': 18.17.17 2720 2968 2721 - string.prototype.matchall@4.0.10: 2969 + string.prototype.matchall@4.0.12: 2722 2970 dependencies: 2723 - call-bind: 1.0.5 2971 + call-bind: 1.0.8 2972 + call-bound: 1.0.4 2724 2973 define-properties: 1.2.1 2725 - es-abstract: 1.22.3 2726 - get-intrinsic: 1.2.2 2727 - has-symbols: 1.0.3 2728 - internal-slot: 1.0.6 2729 - regexp.prototype.flags: 1.5.1 2730 - set-function-name: 2.0.1 2731 - 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 2732 2984 2733 - string.prototype.trim@1.2.8: 2985 + string.prototype.repeat@1.0.0: 2734 2986 dependencies: 2735 - call-bind: 1.0.5 2736 2987 define-properties: 1.2.1 2737 - es-abstract: 1.22.3 2988 + es-abstract: 1.23.9 2738 2989 2739 - string.prototype.trimend@1.0.7: 2990 + string.prototype.trim@1.2.10: 2740 2991 dependencies: 2741 - call-bind: 1.0.5 2992 + call-bind: 1.0.8 2993 + call-bound: 1.0.4 2994 + define-data-property: 1.1.4 2742 2995 define-properties: 1.2.1 2743 - 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 2744 2999 2745 - string.prototype.trimstart@1.0.7: 3000 + string.prototype.trimend@1.0.9: 2746 3001 dependencies: 2747 - call-bind: 1.0.5 3002 + call-bind: 1.0.8 3003 + call-bound: 1.0.4 2748 3004 define-properties: 1.2.1 2749 - es-abstract: 1.22.3 3005 + es-object-atoms: 1.1.1 2750 3006 2751 - string_decoder@1.3.0: 3007 + string.prototype.trimstart@1.0.8: 2752 3008 dependencies: 2753 - safe-buffer: 5.2.1 3009 + call-bind: 1.0.8 3010 + define-properties: 1.2.1 3011 + es-object-atoms: 1.1.1 2754 3012 2755 - strip-ansi@6.0.1: 3013 + string_decoder@1.3.0: 2756 3014 dependencies: 2757 - ansi-regex: 5.0.1 2758 - 2759 - strip-final-newline@2.0.0: {} 2760 - 2761 - strip-final-newline@3.0.0: {} 3015 + safe-buffer: 5.2.1 2762 3016 2763 3017 strip-json-comments@3.1.1: {} 2764 3018 ··· 2768 3022 2769 3023 supports-preserve-symlinks-flag@1.0.0: {} 2770 3024 2771 - synckit@0.8.6: 3025 + synckit@0.11.1: 2772 3026 dependencies: 2773 - '@pkgr/utils': 2.4.2 2774 - tslib: 2.6.2 3027 + '@pkgr/core': 0.2.0 3028 + tslib: 2.8.1 2775 3029 2776 - 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 2777 3044 2778 - 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 2779 3051 2780 3052 to-regex-range@5.0.1: 2781 3053 dependencies: 2782 3054 is-number: 7.0.0 2783 3055 2784 - ts-api-utils@1.0.3(typescript@5.3.2): 3056 + ts-api-utils@2.1.0(typescript@5.8.2): 2785 3057 dependencies: 2786 - typescript: 5.3.2 3058 + typescript: 5.8.2 2787 3059 2788 - tslib@2.6.2: {} 3060 + tslib@2.8.1: {} 2789 3061 2790 3062 type-check@0.4.0: 2791 3063 dependencies: 2792 3064 prelude-ls: 1.2.1 2793 3065 2794 - 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 2795 3071 2796 - typed-array-buffer@1.0.0: 3072 + typed-array-byte-length@1.0.3: 2797 3073 dependencies: 2798 - call-bind: 1.0.5 2799 - get-intrinsic: 1.2.2 2800 - 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 2801 3079 2802 - typed-array-byte-length@1.0.0: 3080 + typed-array-byte-offset@1.0.4: 2803 3081 dependencies: 2804 - call-bind: 1.0.5 2805 - for-each: 0.3.3 2806 - has-proto: 1.0.1 2807 - 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 2808 3089 2809 - typed-array-byte-offset@1.0.0: 3090 + typed-array-length@1.0.7: 2810 3091 dependencies: 2811 - available-typed-arrays: 1.0.5 2812 - call-bind: 1.0.5 2813 - for-each: 0.3.3 2814 - has-proto: 1.0.1 2815 - 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 2816 3098 2817 - typed-array-length@1.0.4: 3099 + typescript-eslint@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2): 2818 3100 dependencies: 2819 - call-bind: 1.0.5 2820 - for-each: 0.3.3 2821 - 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 3108 + 3109 + typescript@5.8.2: {} 2822 3110 2823 - typescript@5.3.2: {} 3111 + ufo@1.5.4: {} 2824 3112 2825 - unbox-primitive@1.0.2: 3113 + unbox-primitive@1.1.0: 2826 3114 dependencies: 2827 - call-bind: 1.0.5 2828 - has-bigints: 1.0.2 2829 - has-symbols: 1.0.3 2830 - 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 2831 3119 2832 - undici-types@6.19.8: {} 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: {} 2833 3128 2834 - untildify@4.0.0: {} 3129 + undici-types@6.21.0: {} 2835 3130 2836 3131 uri-js@4.4.1: 2837 3132 dependencies: 2838 3133 punycode: 2.3.1 2839 3134 2840 - utilium@0.7.1: 3135 + utilium@1.10.1: 2841 3136 dependencies: 2842 3137 eventemitter3: 5.0.1 3138 + optionalDependencies: 3139 + '@xterm/xterm': 5.5.0 2843 3140 2844 - which-boxed-primitive@1.0.2: 3141 + which-boxed-primitive@1.1.1: 2845 3142 dependencies: 2846 - is-bigint: 1.0.4 2847 - is-boolean-object: 1.1.2 2848 - is-number-object: 1.0.7 2849 - is-string: 1.0.7 2850 - is-symbol: 1.0.4 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 2851 3148 2852 - which-builtin-type@1.1.3: 3149 + which-builtin-type@1.2.1: 2853 3150 dependencies: 2854 - function.prototype.name: 1.1.6 2855 - has-tostringtag: 1.0.0 2856 - is-async-function: 2.0.0 2857 - is-date-object: 1.0.5 2858 - is-finalizationregistry: 1.0.2 2859 - is-generator-function: 1.0.10 2860 - is-regex: 1.1.4 2861 - is-weakref: 1.0.2 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 2862 3160 isarray: 2.0.5 2863 - which-boxed-primitive: 1.0.2 2864 - which-collection: 1.0.1 2865 - 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 2866 3164 2867 - which-collection@1.0.1: 3165 + which-collection@1.0.2: 2868 3166 dependencies: 2869 - is-map: 2.0.2 2870 - is-set: 2.0.2 2871 - is-weakmap: 2.0.1 2872 - 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 2873 3171 2874 - which-typed-array@1.1.13: 3172 + which-typed-array@1.1.19: 2875 3173 dependencies: 2876 - available-typed-arrays: 1.0.5 2877 - call-bind: 1.0.5 2878 - for-each: 0.3.3 2879 - gopd: 1.0.1 2880 - 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 2881 3181 2882 3182 which@2.0.2: 2883 3183 dependencies: 2884 3184 isexe: 2.0.0 2885 3185 2886 - wrappy@1.0.2: {} 2887 - 2888 - yallist@4.0.0: {} 3186 + yaml@2.7.1: {} 2889 3187 2890 3188 yocto-queue@0.1.0: {} 3189 + 3190 + zustand@5.0.3(@types/react@18.3.20): 3191 + optionalDependencies: 3192 + '@types/react': 18.3.20
+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/
-70
scripts/link.js
··· 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 onDisk = { 9 - "@moonlight-mod/lunast": "../lunast", 10 - "@moonlight-mod/moonmap": "../moonmap", 11 - "@moonlight-mod/mappings": "../mappings" 12 - }; 13 - 14 - function exec(cmd, dir) { 15 - child_process.execSync(cmd, { cwd: dir, stdio: "inherit" }); 16 - } 17 - 18 - function getDeps(packageJSON) { 19 - const ret = {}; 20 - Object.assign(ret, packageJSON.dependencies || {}); 21 - Object.assign(ret, packageJSON.devDependencies || {}); 22 - Object.assign(ret, packageJSON.peerDependencies || {}); 23 - return ret; 24 - } 25 - 26 - function link(dir) { 27 - const packageJSON = JSON.parse( 28 - fs.readFileSync(path.join(dir, "package.json"), "utf8") 29 - ); 30 - const deps = getDeps(packageJSON); 31 - 32 - for (const [dep, path] of Object.entries(onDisk)) { 33 - if (deps[dep]) { 34 - exec(`pnpm link ${path}`, dir); 35 - } 36 - } 37 - } 38 - 39 - function undo(dir) { 40 - exec("pnpm unlink", dir); 41 - try { 42 - exec("git restore pnpm-lock.yaml", dir); 43 - } catch { 44 - // ignored 45 - } 46 - } 47 - 48 - const shouldUndo = process.argv.includes("--undo"); 49 - const packages = fs.readdirSync("./packages"); 50 - 51 - for (const path of Object.values(onDisk)) { 52 - console.log(path); 53 - if (shouldUndo) { 54 - undo(path); 55 - } else { 56 - link(path); 57 - } 58 - } 59 - 60 - if (shouldUndo) { 61 - const dir = __dirname; 62 - console.log(dir); 63 - undo(dir); 64 - } else { 65 - for (const pkg of packages) { 66 - const dir = path.join(__dirname, "packages", pkg); 67 - console.log(dir); 68 - link(dir); 69 - } 70 - }
+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 + }
-31
scripts/update.js
··· 1 - // Update dependencies in all packages 2 - /* eslint-disable no-console */ 3 - const fs = require("fs"); 4 - const path = require("path"); 5 - const child_process = require("child_process"); 6 - 7 - const packageToUpdate = process.argv[2]; 8 - 9 - function getDeps(packageJSON) { 10 - const ret = {}; 11 - Object.assign(ret, packageJSON.dependencies || {}); 12 - Object.assign(ret, packageJSON.devDependencies || {}); 13 - Object.assign(ret, packageJSON.peerDependencies || {}); 14 - return ret; 15 - } 16 - 17 - function exec(cmd, dir) { 18 - child_process.execSync(cmd, { cwd: dir, stdio: "inherit" }); 19 - } 20 - 21 - for (const package of fs.readdirSync("./packages")) { 22 - const packageJSON = JSON.parse( 23 - fs.readFileSync(path.join("./packages", package, "package.json"), "utf8") 24 - ); 25 - 26 - const deps = getDeps(packageJSON); 27 - if (Object.keys(deps).includes(packageToUpdate)) { 28 - console.log(`Updating ${packageToUpdate} in ${package}`); 29 - exec(`pnpm update ${packageToUpdate}`, path.join("./packages", package)); 30 - } 31 - }
+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 -16
tsconfig.json
··· 1 1 { 2 + "extends": ["./tsconfig.base.json"], 2 3 "compilerOptions": { 3 - "target": "es2022", 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 - // meriyah has a broken import lol 14 - "skipLibCheck": true, 15 - 16 - // disable unreachable code detection because it breaks with esbuild labels 17 - "allowUnreachableCode": true 5 + "noEmit": true 18 6 }, 19 - "include": ["./packages/**/*", "./env.d.ts"], 20 - "exclude": ["node_modules"] 7 + "exclude": [ 8 + "**/node_modules/**", 9 + "**/dist/**", 10 + "**/build/**" 11 + ] 21 12 }