diff --git a/.gitignore b/.gitignore index cf7bd98b8..d05b1773c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules result dist/ +dist-bundle/ .parcel-cache yarn.lock package-lock.json @@ -19,3 +20,4 @@ config.json /docs /*.asar /bin.mjs +/*.d.ts diff --git a/bin/index.mts b/bin/index.mts new file mode 100755 index 000000000..553c5fc6a --- /dev/null +++ b/bin/index.mts @@ -0,0 +1,587 @@ +#!/usr/bin/env node +import asar from "@electron/asar"; +import { + copyFileSync, + cpSync, + existsSync, + mkdirSync, + readFileSync, + rmSync, + writeFileSync, +} from "fs"; +import esbuild from "esbuild"; +import path from "path"; +import updateNotifier from "update-notifier"; +import yargs, { ArgumentsCamelCase } from "yargs"; +import { hideBin } from "yargs/helpers"; +import chalk from "chalk"; +import WebSocket from "ws"; +import { release } from "./release.mjs"; +import { logBuildPlugin } from "../src/util.mjs"; +import { sassPlugin } from "esbuild-sass-plugin"; +import { fileURLToPath } from "url"; +import { AddonType, getAddonFolder, isMonoRepo, selectAddon } from "./mono.mjs"; + +interface BaseArgs { + watch?: boolean; + noInstall?: boolean; + production?: boolean; + noReload?: boolean; + addon?: string; +} + +type Args = ArgumentsCamelCase | BaseArgs; + +export const directory = process.cwd(); + +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const packageJson = JSON.parse(readFileSync(path.resolve(dirname, "package.json"), "utf-8")); + +const updateMessage = `Update available ${chalk.dim("{currentVersion}")}${chalk.reset( + " → ", +)}${chalk.green("{latestVersion}")} \nRun ${chalk.cyan("pnpm i -D replugged")} to update`; + +const notifier = updateNotifier({ + pkg: packageJson, + shouldNotifyInNpmScript: true, +}); + +function sendUpdateNotification(): void { + notifier.notify({ + message: updateMessage, + }); +} + +const MIN_PORT = 6463; +const MAX_PORT = 6472; + +function random(): string { + return Math.random().toString(16).slice(2); +} + +let ws: WebSocket | undefined; +let failed = false; +let connectingPromise: Promise | undefined; + +/** + * Try to connect to RPC on a specific port and handle the READY event as well as errors and close events + */ +function tryPort(port: number): Promise { + ws = new WebSocket(`ws://127.0.0.1:${port}/?v=1&client_id=REPLUGGED-${random()}`); + return new Promise((resolve, reject) => { + let didFinish = false; + ws?.on("message", (data) => { + if (didFinish) { + return; + } + + const message = JSON.parse(data.toString()); + if (message.evt !== "READY") { + return; + } + + didFinish = true; + + resolve(ws); + }); + ws?.on("error", () => { + if (didFinish) { + return; + } + + didFinish = true; + + reject(new Error("WebSocket error")); + }); + ws?.on("close", () => { + ws = undefined; + + if (didFinish) { + return; + } + + didFinish = true; + + reject(new Error("WebSocket closed")); + }); + }); +} + +/** + * Get an active websocket connection to Discord. If one is already open, it will be returned. Otherwise, a new connection will be made. + * If a connection cannot be made or failed previously, none will be made and undefined will be returned. + */ +async function connectWebsocket(): Promise { + if (ws && ws.readyState === WebSocket.OPEN) { + return ws; + } + if (failed) return null; + if (connectingPromise) return await connectingPromise; + + connectingPromise = (async () => { + for (let port = MIN_PORT; port <= MAX_PORT; port++) { + try { + ws = await tryPort(port); + return ws; + } catch {} + } + return undefined; + })(); + + const result = await connectingPromise; + connectingPromise = undefined; + if (result) { + return result; + } + + console.error("Could not connect to Discord websocket"); + failed = true; + return undefined; +} + +let reloading = false; +let reloadAgain = false; + +/** + * Send WS request to reload an addon + */ +async function reload(id: string): Promise { + const ws = await connectWebsocket(); + if (!ws) return; + + if (reloading) { + reloadAgain = true; + return; + } + + const nonce = random(); + + ws.send( + JSON.stringify({ + cmd: "REPLUGGED_ADDON_WATCHER", + args: { + id, + }, + nonce, + }), + ); + + reloading = true; + + await new Promise((resolve) => { + /** + * + * @param {import('ws').RawData} data + * @returns + */ + const onMessage = async (data: string): Promise => { + const message = JSON.parse(data.toString()); + if (message.nonce !== nonce) { + return; + } + ws.off("message", onMessage); + + reloading = false; + if (reloadAgain) { + reloadAgain = false; + resolve(await reload(id)); + return; + } + + if (message.data.success) { + console.log("Reloaded addon"); + resolve(undefined); + } else { + const errorCode = message.data.error; + let error = "Unknown error"; + switch (errorCode) { + case "ADDON_NOT_FOUND": + error = "Addon not found"; + break; + case "ADDON_DISABLED": + error = "Addon disabled"; + break; + case "RELOAD_FAILED": + error = "Reload failed"; + break; + } + console.error(`Failed to reload addon: ${error}`); + resolve(undefined); + } + }; + + ws.on("message", onMessage); + }); +} + +function bundleAddons(buildFn: (args: Args) => Promise, type: AddonType): void { + const addons = getAddonFolder(type); + + addons.forEach((addon) => bundleAddon(buildFn, addon, type)); +} + +async function bundleAddon( + buildFn: (args: Args) => Promise, + addon?: string, + type?: AddonType, +): Promise { + const manifestPath = addon + ? path.join(directory, type!, addon, "manifest.json") + : path.join(directory, "manifest.json"); + const manifest = JSON.parse(readFileSync(manifestPath, "utf-8")); + const distPath = addon ? `dist/${manifest.id}` : "dist"; + if (existsSync(distPath)) { + rmSync(distPath, { recursive: true }); + } + await buildFn({ watch: false, noInstall: true, production: true, addon }); + + const outputName = `bundle/${manifest.id}`; + + if (!existsSync("bundle")) { + mkdirSync("bundle"); + } + asar.createPackage(distPath, `${outputName}.asar`); + copyFileSync(`${distPath}/manifest.json`, `${outputName}.json`); + + console.log(`Bundled ${manifest.name}`); +} + +async function handleContexts( + contexts: esbuild.BuildContext[], + watch: boolean | undefined, +): Promise { + await Promise.all( + contexts.map(async (context) => { + if (watch) { + await context.watch(); + } else { + await context.rebuild().catch(() => {}); + context.dispose(); + } + }), + ); +} + +const REPLUGGED_FOLDER_NAME = "replugged"; +const CONFIG_PATH = (() => { + switch (process.platform) { + case "win32": + return path.join(process.env.APPDATA || "", REPLUGGED_FOLDER_NAME); + case "darwin": + return path.join( + process.env.HOME || "", + "Library", + "Application Support", + REPLUGGED_FOLDER_NAME, + ); + default: + if (process.env.XDG_CONFIG_HOME) { + return path.join(process.env.XDG_CONFIG_HOME, REPLUGGED_FOLDER_NAME); + } + return path.join(process.env.HOME || "", ".config", REPLUGGED_FOLDER_NAME); + } +})(); +const CHROME_VERSION = "91"; + +function buildAddons(buildFn: (args: Args) => Promise, args: Args, type: AddonType): void { + const addons = getAddonFolder(type); + + addons.forEach((addon) => { + buildFn({ ...args, addon }); + }); +} + +async function buildPlugin({ watch, noInstall, production, noReload, addon }: Args): Promise { + const manifestPath = addon + ? path.join(directory, "plugins", addon, "manifest.json") + : path.join(directory, "manifest.json"); + const manifest = JSON.parse(readFileSync(manifestPath.toString(), "utf-8")); + const distPath = addon ? `dist/${manifest.id}` : "dist"; + const folderPath = addon ? path.join(directory, "plugins", addon) : directory; + + const globalModules: esbuild.Plugin = { + name: "globalModules", + setup: (build) => { + build.onResolve({ filter: /^replugged(\/\w+)?$/ }, (args) => { + if (args.kind !== "import-statement") return undefined; + + if (args.path.includes("dist")) { + return { + errors: [ + { + text: `Unsupported import from dist: ${args.path}\nImport from either the top level of this module ("replugged") or a top-level subpath (e.g. "replugged/common") instead.`, + }, + ], + }; + } + + return { + path: args.path, + namespace: "replugged", + }; + }); + + build.onResolve({ filter: /^react$/ }, (args) => { + if (args.kind !== "import-statement") return undefined; + + return { + path: "replugged/common/React", + namespace: "replugged", + }; + }); + + build.onLoad( + { + filter: /.*/, + namespace: "replugged", + }, + (loadArgs) => { + return { + contents: `module.exports = window.${loadArgs.path.replaceAll("/", ".")}`, + }; + }, + ); + }, + }; + + const install: esbuild.Plugin = { + name: "install", + setup: (build) => { + build.onEnd(async () => { + if (!noInstall) { + const dest = path.join(CONFIG_PATH, "plugins", manifest.id); + if (existsSync(dest)) rmSync(dest, { recursive: true, force: true }); + cpSync(distPath, dest, { recursive: true }); + console.log("Installed updated version"); + + if (!noReload) { + await reload(manifest.id); + } + } + }); + }, + }; + + const plugins: esbuild.Plugin[] = [globalModules, install]; + if (watch) plugins.push(logBuildPlugin); + + const common: esbuild.BuildOptions = { + absWorkingDir: directory, + bundle: true, + format: "esm", + logLevel: "info", + minify: production, + platform: "browser", + plugins, + sourcemap: !production, + target: `chrome${CHROME_VERSION}`, + }; + + const targets: Array> = []; + + if ("renderer" in manifest) { + targets.push( + esbuild.context({ + ...common, + entryPoints: [path.join(folderPath, manifest.renderer)], + outfile: `${distPath}/renderer.js`, + }), + ); + + manifest.renderer = "renderer.js"; + } + + if ("plaintextPatches" in manifest) { + targets.push( + esbuild.context({ + ...common, + entryPoints: [path.join(folderPath, manifest.plaintextPatches)], + outfile: `${distPath}/plaintextPatches.js`, + }), + ); + + manifest.plaintextPatches = "plaintextPatches.js"; + } + + if (!existsSync(distPath)) mkdirSync(distPath, { recursive: true }); + + writeFileSync(`${distPath}/manifest.json`, JSON.stringify(manifest)); + + const contexts = await Promise.all(targets); + await handleContexts(contexts, watch); + + ws?.close(); +} + +async function buildTheme({ watch, noInstall, production, noReload, addon }: Args): Promise { + const manifestPath = addon + ? path.join(directory, "themes", addon, "manifest.json") + : "manifest.json"; + const manifest = JSON.parse(readFileSync(manifestPath.toString(), "utf-8")); + const distPath = addon ? `dist/${manifest.id}` : "dist"; + const folderPath = addon ? path.join(directory, "themes", addon) : directory; + + const main = path.join(folderPath, manifest.main || "src/main.css"); + const splash = existsSync(path.join(folderPath, manifest.splash || "src/main.css")) + ? path.join(folderPath, manifest.splash || "src/main.css") + : undefined; + + const install: esbuild.Plugin = { + name: "install", + setup: (build) => { + build.onEnd(async () => { + if (!noInstall) { + const dest = path.join(CONFIG_PATH, "themes", manifest.id); + if (existsSync(dest)) rmSync(dest, { recursive: true, force: true }); + cpSync(distPath, dest, { recursive: true }); + console.log("Installed updated version"); + + if (!noReload) { + await reload(manifest.id); + } + } + }); + }, + }; + + const plugins: esbuild.Plugin[] = [sassPlugin(), install]; + if (watch) plugins.push(logBuildPlugin); + + const common: esbuild.BuildOptions = { + absWorkingDir: directory, + bundle: true, + format: "esm", + logLevel: "info", + minify: production, + platform: "browser", + plugins, + sourcemap: !production, + target: `chrome${CHROME_VERSION}`, + }; + + const targets: Array> = []; + + if (main) { + targets.push( + esbuild.context({ + ...common, + entryPoints: [main], + outfile: `${distPath}/main.css`, + }), + ); + + manifest.main = "main.css"; + } + + if (splash) { + targets.push( + esbuild.context({ + ...common, + entryPoints: [splash], + outfile: `${distPath}/splash.css`, + }), + ); + + manifest.plaintextPatches = "splash.css"; + } + + if (!existsSync(distPath)) mkdirSync(distPath, { recursive: true }); + + writeFileSync(`${distPath}/manifest.json`, JSON.stringify(manifest)); + + const contexts = await Promise.all(targets); + await handleContexts(contexts, watch); + + ws?.close(); +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const { argv } = yargs(hideBin(process.argv)) + .scriptName("replugged") + .usage("$0 [args]") + .command( + "build ", + "Build an Addon", + (yargs) => { + yargs.positional("addon", { + type: "string", + describe: "Either a plugin or theme", + }); + yargs.option("no-install", { + type: "boolean", + describe: "Don't install the built addon", + default: false, + }); + yargs.option("watch", { + type: "boolean", + describe: "Watch the addon for changes to reload building", + default: false, + }); + yargs.option("production", { + type: "boolean", + describe: "Don't compile the source maps when building.", + default: false, + }); + yargs.option("no-reload", { + type: "boolean", + describe: "Don't reload the addon in Discord after building.", + default: false, + }); + yargs.option("all", { + type: "boolean", + describe: "Build all addons in a monorepo.", + default: false, + }); + }, + async (argv) => { + if (argv.addon === "plugin") { + if (argv.all && isMonoRepo) return buildAddons(buildPlugin, argv, "plugins"); + else { + const plugin = isMonoRepo ? await selectAddon("plugins") : undefined; + buildPlugin({ ...argv, addon: plugin?.name }); + } + } else if (argv.addon === "theme") { + if (argv.all && isMonoRepo) return buildAddons(buildTheme, argv, "themes"); + else { + const theme = isMonoRepo ? await selectAddon("themes") : undefined; + buildTheme({ ...argv, addon: theme?.name }); + } + } else { + console.log("Invalid addon type."); + } + sendUpdateNotification(); + }, + ) + .command( + "bundle ", + "Bundle any Addon", + (yargs) => { + yargs.positional("addon", { + type: "string", + describe: "Either a plugin or theme", + }); + }, + async (argv) => { + if (argv.addon === "plugin") { + if (argv.all && isMonoRepo) return bundleAddons(buildPlugin, "plugins"); + else { + const addon = isMonoRepo ? await selectAddon("plugins") : undefined; + bundleAddon(buildPlugin, addon?.name); + } + } else if (argv.addon === "theme") { + if (argv.all && isMonoRepo) return bundleAddons(buildTheme, "themes"); + else { + const addon = isMonoRepo ? await selectAddon("themes") : undefined; + bundleAddon(buildTheme, addon?.name); + } + } else { + console.log("Invalid addon type."); + } + sendUpdateNotification(); + }, + ) + .command("release", "Interactively release a new version of an addon", () => {}, release) + .parserConfiguration({ + "boolean-negation": false, + }) + .help(); diff --git a/bin/mono.mts b/bin/mono.mts new file mode 100644 index 000000000..693107b4a --- /dev/null +++ b/bin/mono.mts @@ -0,0 +1,82 @@ +#!/usr/bin/env node + +import { existsSync, readdirSync } from "fs"; +import path from "path"; +import { directory } from "./index.mjs"; +import prompts from "prompts"; +import { onCancel } from "./release.mjs"; + +export const isMonoRepo = + existsSync(path.join(process.cwd(), "plugins")) || existsSync(path.join(process.cwd(), "themes")); + +export type AddonType = "plugins" | "themes"; + +export function getAddonFolder(type: AddonType): string[] { + if (!existsSync(path.join(directory, type))) return []; + const folder = readdirSync(path.join(directory, type), { withFileTypes: true }); + + folder.filter((dirent) => dirent.isDirectory()); + + return folder.map((direct) => direct.name); +} + +interface SelectedAddon { + type: AddonType; + name: string; +} + +export async function selectAddon(type: AddonType | "all"): Promise { + if (type !== "all") { + const folder = getAddonFolder(type as AddonType); + + const { addon } = await prompts( + { + type: "select", + name: "addon", + message: "Select an addon", + choices: folder.map((folderName) => ({ + title: folderName, + value: { type, name: folderName }, + })), + }, + { onCancel }, + ); + + return addon; + } else { + const plugins: string[] = []; + const themes: string[] = []; + + if (existsSync(path.join(directory, "plugins"))) { + readdirSync(path.join(directory, "plugins"), { withFileTypes: true }).forEach((dirent) => { + if (dirent.isDirectory()) plugins.push(dirent.name); + }); + } + if (existsSync(path.join(directory, "themes"))) { + readdirSync(path.join(directory, "themes"), { withFileTypes: true }).forEach((dirent) => { + if (dirent.isDirectory()) themes.push(dirent.name); + }); + } + + const { addon } = await prompts( + { + type: "select", + name: "addon", + message: "Select an addon", + choices: [ + ...plugins.map((plugin) => ({ + title: `${plugin} (plugin)`, + value: { type: "plugins", name: plugin }, + })), + ...themes.map((theme) => ({ + title: `${theme} (theme)`, + value: { type: "themes", name: theme }, + })), + ], + }, + { onCancel }, + ); + + return addon; + } +} diff --git a/bin/release.mjs b/bin/release.mts similarity index 80% rename from bin/release.mjs rename to bin/release.mts index fbcebc7a8..9343e6ee0 100644 --- a/bin/release.mjs +++ b/bin/release.mts @@ -1,20 +1,15 @@ -#!/usr/bin/env node -/* eslint-disable no-process-exit */ - import { existsSync, readFileSync, writeFileSync } from "fs"; import path from "path"; import prompts from "prompts"; import semver from "semver"; import chalk from "chalk"; import { execSync } from "child_process"; +import { isMonoRepo, selectAddon } from "./mono.mjs"; /** * Prompt a confirmation message and exit if the user does not confirm. - * - * @param {string} message - * @param {boolean} [initial] */ -async function confirmOrExit(message, initial = false) { +async function confirmOrExit(message: string, initial = false): Promise { const { doContinue } = await prompts( { type: "confirm", @@ -33,12 +28,8 @@ async function confirmOrExit(message, initial = false) { /** * Run a command and return the output. - * - * @param {string} command - * @param {boolean} [exit = true] Exit if the command fails - * @returns {string} */ -function runCommand(command, exit = true) { +function runCommand(command: string, exit = true): string { try { const result = execSync(command, { encoding: "utf8", @@ -46,24 +37,22 @@ function runCommand(command, exit = true) { }); return result; } catch (error) { - // @ts-expect-error + // @ts-expect-error not unknown if (!exit) return error.stdout; - // @ts-expect-error + // @ts-expect-error not unknown console.error(error.message); process.exit(1); } - throw new Error("Unreachable"); } -function onCancel() { +export function onCancel(): void { console.log(chalk.red("Aborting")); process.exit(128); // SIGINT } -/** @type {string} */ -let root; +let root: string; -function getRootDir() { +function getRootDir(): string { if (root) return root; try { @@ -73,13 +62,13 @@ function getRootDir() { }).trim(); return root; } catch (error) { - // @ts-expect-error + // @ts-expect-error not unknown if (error.message.includes("not a git repository")) { console.log(chalk.red("You must run this command from within a git repository")); process.exit(1); } - // @ts-expect-error + // @ts-expect-error not unknown console.error(`Command failed with exit code ${error.status}: ${error.message}`); process.exit(1); } @@ -87,14 +76,17 @@ function getRootDir() { throw new Error("Unreachable"); } -export async function release() { +export async function release(): Promise { const directory = getRootDir(); const status = runCommand("git status --porcelain"); const isClean = !status.trim(); if (!isClean) await confirmOrExit("Working directory is not clean. Continue?"); - const manifestPath = path.resolve(directory, "manifest.json"); + const addon = isMonoRepo ? await selectAddon("all") : null; + const manifestPath = addon + ? path.resolve(directory, addon.type, addon.name, "manifest.json") + : path.resolve(directory, "manifest.json"); if (!existsSync(manifestPath)) { console.log(chalk.red("manifest.json not found")); process.exit(1); @@ -108,15 +100,15 @@ export async function release() { process.exit(1); } - const packagePath = path.resolve(directory, "package.json"); - if (!existsSync(packagePath)) { + const packagePath = addon ? null : path.resolve(directory, "package.json"); + if (!isMonoRepo && !existsSync(packagePath!)) { console.log(chalk.red("package.json not found")); process.exit(1); } - const packageText = readFileSync(packagePath, "utf8"); + const packageText = packagePath ? readFileSync(packagePath, "utf8") : null; let packageJson; try { - packageJson = JSON.parse(packageText); + packageJson = packagePath ? JSON.parse(packageText!) : null; } catch { console.log(chalk.red("package.json is not valid JSON")); process.exit(1); @@ -201,14 +193,15 @@ export async function release() { // Update manifest.json and package.json manifest.version = nextVersion; - packageJson.version = nextVersion; + if (packageJson) packageJson.version = nextVersion; // Write manifest.json and package.json (indent with 2 spaces and keep trailing newline) writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); - writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`); + if (packageJson) writeFileSync(packagePath!, `${JSON.stringify(packageJson, null, 2)}\n`); // Stage changes - runCommand("git add manifest.json package.json"); + if (isMonoRepo) runCommand(`git add ${path.join(addon!.type, addon!.name, "manifest.json")}`); + else runCommand("git add manifest.json package.json"); // Commit changes const { message } = await prompts( @@ -216,7 +209,9 @@ export async function release() { type: "text", name: "message", message: "Commit message", - initial: `Release v${nextVersion}`, + initial: isMonoRepo + ? `[${manifest.name}] Release v${nextVersion}` + : `Release v${nextVersion}`, validate: (value) => { if (!value.trim()) return "Commit message is required"; @@ -234,7 +229,9 @@ export async function release() { type: "text", name: "tagName", message: "Tag name", - initial: `v${nextVersion}`, + initial: isMonoRepo + ? `v${nextVersion}-${manifest.name.replace(" ", "_")}` + : `v${nextVersion}`, validate: (value) => { if (!value.trim()) return "Tag name is required"; diff --git a/bin/replugged.mjs b/bin/replugged.mjs deleted file mode 100755 index 378149371..000000000 --- a/bin/replugged.mjs +++ /dev/null @@ -1,574 +0,0 @@ -#!/usr/bin/env node - -import asar from "@electron/asar"; -import { Parcel } from "@parcel/core"; -import { - copyFileSync, - cpSync, - existsSync, - mkdirSync, - readFileSync, - rmSync, - writeFileSync, -} from "fs"; -import esbuild from "esbuild"; -import path from "path"; -import updateNotifier from "update-notifier"; -import yargs from "yargs"; -import { hideBin } from "yargs/helpers"; -import chalk from "chalk"; -import WebSocket from "ws"; -import { fileURLToPath, pathToFileURL } from "url"; -import { release } from "./release.mjs"; - -const dirname = path.dirname(fileURLToPath(import.meta.url)); -const directory = process.cwd(); -const manifestPath = pathToFileURL(path.join(directory, "manifest.json")); - -const packageJson = JSON.parse(readFileSync(path.resolve(dirname, "../package.json"), "utf-8")); - -const updateMessage = `Update available ${chalk.dim("{currentVersion}")}${chalk.reset( - " → ", -)}${chalk.green("{latestVersion}")} \nRun ${chalk.cyan("pnpm i -D replugged")} to update`; - -const notifier = updateNotifier({ - pkg: packageJson, - shouldNotifyInNpmScript: true, -}); - -function sendUpdateNotification() { - notifier.notify({ - message: updateMessage, - }); -} - -const MIN_PORT = 6463; -const MAX_PORT = 6472; - -function random() { - return Math.random().toString(16).slice(2); -} - -/** - * @type {WebSocket | undefined} - */ -let ws; -let failed = false; -/** - * @type {Promise | undefined} - */ -let connectingPromise; - -/** - * Try to connect to RPC on a specific port and handle the READY event as well as errors and close events - * @param {number} port - */ -function tryPort(port) { - ws = new WebSocket(`ws://127.0.0.1:${port}/?v=1&client_id=REPLUGGED-${random()}`); - return new Promise((resolve, reject) => { - let didFinish = false; - ws?.on("message", (data) => { - if (didFinish) { - return; - } - - const message = JSON.parse(data.toString()); - if (message.evt !== "READY") { - return; - } - - didFinish = true; - - resolve(ws); - }); - ws?.on("error", () => { - if (didFinish) { - return; - } - - didFinish = true; - - reject(new Error("WebSocket error")); - }); - ws?.on("close", () => { - ws = undefined; - - if (didFinish) { - return; - } - - didFinish = true; - - reject(new Error("WebSocket closed")); - }); - }); -} - -/** - * Get an active websocket connection to Discord. If one is already open, it will be returned. Otherwise, a new connection will be made. - * If a connection cannot be made or failed previously, none will be made and undefined will be returned. - */ -async function connectWebsocket() { - if (ws && ws.readyState === WebSocket.OPEN) { - return ws; - } - if (failed) return null; - if (connectingPromise) return await connectingPromise; - - connectingPromise = (async () => { - for (let port = MIN_PORT; port <= MAX_PORT; port++) { - try { - ws = await tryPort(port); - return ws; - } catch {} - } - return undefined; - })(); - - const result = await connectingPromise; - connectingPromise = undefined; - if (result) { - return result; - } - - console.error("Could not connect to Discord websocket"); - failed = true; - return undefined; -} - -let reloading = false; -let reloadAgain = false; - -/** - * Send WS request to reload an addon - * @param {string} id - */ -async function reload(id) { - const ws = await connectWebsocket(); - if (!ws) return; - - if (reloading) { - reloadAgain = true; - return; - } - - const nonce = random(); - - ws.send( - JSON.stringify({ - cmd: "REPLUGGED_ADDON_WATCHER", - args: { - id, - }, - nonce, - }), - ); - - reloading = true; - - await new Promise((resolve) => { - /** - * - * @param {import('ws').RawData} data - * @returns - */ - const onMessage = async (data) => { - const message = JSON.parse(data.toString()); - if (message.nonce !== nonce) { - return; - } - ws.off("message", onMessage); - - reloading = false; - if (reloadAgain) { - reloadAgain = false; - resolve(await reload(id)); - return; - } - - if (message.data.success) { - console.log("Reloaded addon"); - resolve(undefined); - } else { - const errorCode = message.data.error; - let error = "Unknown error"; - switch (errorCode) { - case "ADDON_NOT_FOUND": - error = "Addon not found"; - break; - case "ADDON_DISABLED": - error = "Addon disabled"; - break; - case "RELOAD_FAILED": - error = "Reload failed"; - break; - } - console.error(`Failed to reload addon: ${error}`); - resolve(undefined); - } - }; - - ws.on("message", onMessage); - }); -} - -/** - * @typedef Args - * @property {boolean} [watch] - * @property {boolean} [noInstall] - * @property {boolean} [production] - * @property {boolean} [noReload] - */ - -/** - * @param {(args: Args) => Promise} buildFn - */ -async function bundleAddon(buildFn) { - if (existsSync("dist")) { - rmSync("dist", { recursive: true }); - } - await buildFn({ watch: false, noInstall: true, production: true }); - - const manifest = JSON.parse(readFileSync("dist/manifest.json", "utf-8")); - const outputName = `bundle/${manifest.id}`; - - if (!existsSync("bundle")) { - mkdirSync("bundle"); - } - asar.createPackage("dist", `${outputName}.asar`); - copyFileSync("dist/manifest.json", `${outputName}.json`); - - console.log(`Bundled ${manifest.name}`); -} - -/** - * @param {Args} args - */ -async function buildPlugin({ watch, noInstall, production, noReload }) { - // @ts-expect-error - let manifest = await import(manifestPath.toString(), { - assert: { type: "json" }, - }); - if ("default" in manifest) manifest = manifest.default; - const CHROME_VERSION = "91"; - const REPLUGGED_FOLDER_NAME = "replugged"; - const globalModules = { - name: "globalModules", - // @ts-expect-error - setup: (build) => { - // @ts-expect-error - build.onResolve({ filter: /^replugged.+$/ }, (args) => { - if (args.kind !== "import-statement") return undefined; - - return { - errors: [ - { - text: `Importing from a path (${args.path}) is not supported. Instead, please import from "replugged" and destructure the required modules.`, - }, - ], - }; - }); - - // @ts-expect-error - build.onResolve({ filter: /^replugged$/ }, (args) => { - if (args.kind !== "import-statement") return undefined; - - return { - path: args.path, - namespace: "replugged", - }; - }); - - build.onLoad( - { - filter: /.*/, - namespace: "replugged", - }, - () => { - return { - contents: "module.exports = window.replugged", - }; - }, - ); - }, - }; - - const CONFIG_PATH = (() => { - switch (process.platform) { - case "win32": - return path.join(process.env.APPDATA || "", REPLUGGED_FOLDER_NAME); - case "darwin": - return path.join( - process.env.HOME || "", - "Library", - "Application Support", - REPLUGGED_FOLDER_NAME, - ); - default: - if (process.env.XDG_CONFIG_HOME) { - return path.join(process.env.XDG_CONFIG_HOME, REPLUGGED_FOLDER_NAME); - } - return path.join(process.env.HOME || "", ".config", REPLUGGED_FOLDER_NAME); - } - })(); - - const install = { - name: "install", - // @ts-expect-error - setup: (build) => { - build.onEnd(async () => { - if (!noInstall) { - const dest = path.join(CONFIG_PATH, "plugins", manifest.id); - if (existsSync(dest)) rmSync(dest, { recursive: true, force: true }); - cpSync("dist", dest, { recursive: true }); - console.log("Installed updated version"); - - if (!noReload) { - await reload(manifest.id); - } - } - }); - }, - }; - - const common = { - absWorkingDir: directory, - bundle: true, - format: "esm", - logLevel: "info", - minify: production, - platform: "browser", - plugins: [globalModules, install], - sourcemap: !production, - target: `chrome${CHROME_VERSION}`, - watch, - }; - - const targets = []; - - if ("renderer" in manifest) { - targets.push( - // @ts-expect-error - esbuild.build({ - ...common, - entryPoints: [manifest.renderer], - outfile: "dist/renderer.js", - }), - ); - - manifest.renderer = "renderer.js"; - } - - if ("plaintextPatches" in manifest) { - targets.push( - // @ts-expect-error - esbuild.build({ - ...common, - entryPoints: [manifest.plaintextPatches], - outfile: "dist/plaintextPatches.js", - }), - ); - - manifest.plaintextPatches = "plaintextPatches.js"; - } - - if (!existsSync("dist")) mkdirSync("dist"); - - writeFileSync("dist/manifest.json", JSON.stringify(manifest)); - - await Promise.all(targets); - - ws?.close(); -} - -/** - * @param {Args} args - */ -async function buildTheme({ watch: shouldWatch, noInstall, production, noReload }) { - // @ts-expect-error - let manifest = await import(manifestPath, { - assert: { type: "json" }, - }); - if ("default" in manifest) manifest = manifest.default; - - const main = manifest.main || "src/main.css"; - const splash = manifest.splash || (existsSync("src/splash.css") ? "src/splash.css" : undefined); - - const mainBundler = new Parcel({ - entries: main, - defaultConfig: "@parcel/config-default", - targets: { - main: { - distDir: "dist", - distEntry: "main.css", - sourceMap: !production, - optimize: production, - }, - }, - }); - - const splashBundler = splash - ? new Parcel({ - entries: splash, - defaultConfig: "@parcel/config-default", - targets: { - main: { - distDir: "dist", - distEntry: "splash.css", - }, - }, - }) - : undefined; - - const REPLUGGED_FOLDER_NAME = "replugged"; - const CONFIG_PATH = (() => { - switch (process.platform) { - case "win32": - return path.join(process.env.APPDATA || "", REPLUGGED_FOLDER_NAME); - case "darwin": - return path.join( - process.env.HOME || "", - "Library", - "Application Support", - REPLUGGED_FOLDER_NAME, - ); - default: - if (process.env.XDG_CONFIG_HOME) { - return path.join(process.env.XDG_CONFIG_HOME, REPLUGGED_FOLDER_NAME); - } - return path.join(process.env.HOME || "", ".config", REPLUGGED_FOLDER_NAME); - } - })(); - - async function install() { - if (!noInstall) { - const dest = path.join(CONFIG_PATH, "themes", manifest.id); - if (existsSync(dest)) { - rmSync(dest, { recursive: true, force: true }); - } - cpSync("dist", dest, { recursive: true }); - console.log("Installed updated version"); - - if (!noReload) { - // @ts-expect-error - await reload(manifest.id, watch); - } - } - } - - // @ts-expect-error - async function build(bundler) { - const { bundleGraph, buildTime } = await bundler.run(); - let bundles = bundleGraph.getBundles(); - console.log(`Built ${bundles.length} bundles in ${buildTime}ms!`); - install(); - } - - // @ts-expect-error - async function watch(bundler) { - // @ts-expect-error - await bundler.watch((err, event) => { - if (err) { - // fatal error - throw err; - } - if (!event) return; - - if (event.type === "buildSuccess") { - let bundles = event.bundleGraph.getBundles(); - console.log(`✨ Built ${bundles.length} bundles in ${event.buildTime}ms!`); - install(); - } else if (event.type === "buildFailure") { - console.log(event.diagnostics); - } - }); - } - - const fn = shouldWatch ? watch : build; - const promises = [mainBundler, splashBundler].filter(Boolean).map((bundler) => fn(bundler)); - - manifest.main = "main.css"; - manifest.splash = splash ? "splash.css" : undefined; - - if (!existsSync("dist")) { - mkdirSync("dist"); - } - - writeFileSync("dist/manifest.json", JSON.stringify(manifest)); - - await Promise.all(promises); - - ws?.close(); -} - -// eslint-disable-next-line no-unused-vars -const { argv } = yargs(hideBin(process.argv)) - .scriptName("replugged") - .usage("$0 [args]") - .command( - "build ", - "Build an Addon", - (yargs) => { - yargs.positional("addon", { - type: "string", - describe: "Either a plugin or theme", - }); - yargs.option("no-install", { - type: "boolean", - describe: "Don't install the built addon", - default: false, - }); - yargs.option("watch", { - type: "boolean", - describe: "Watch the addon for changes to reload building", - default: false, - }); - yargs.option("production", { - type: "boolean", - describe: "Don't compile the source maps when building.", - default: false, - }); - yargs.option("no-reload", { - type: "boolean", - describe: "Don't reload the addon in Discord after building.", - default: false, - }); - }, - (argv) => { - if (argv.addon === "plugin") { - // @ts-expect-error - buildPlugin(argv); - } else if (argv.addon === "theme") { - // @ts-expect-error - buildTheme(argv); - } else { - console.log("Invalid addon type."); - } - sendUpdateNotification(); - }, - ) - .command( - "bundle ", - "Bundle any Addon", - (yargs) => { - yargs.positional("addon", { - type: "string", - describe: "Either a plugin or theme", - }); - }, - (argv) => { - if (argv.addon === "plugin") { - bundleAddon(buildPlugin); - } else if (argv.addon === "theme") { - bundleAddon(buildTheme); - } else { - console.log("Invalid addon type."); - } - sendUpdateNotification(); - }, - ) - .command("release", "Interactively release a new version of an addon", () => {}, release) - .parserConfiguration({ - "boolean-negation": false, - }) - .help(); diff --git a/cspell.json b/cspell.json index e586926d8..2e4895344 100644 --- a/cspell.json +++ b/cspell.json @@ -29,6 +29,7 @@ "fontawesome", "Fonticons", "getent", + "gifv", "globstar", "gpgsign", "groupstart", @@ -39,6 +40,7 @@ "lezer", "LOCALAPPDATA", "logname", + "metafile", "Millis", "notif", "notrack", diff --git a/package.json b/package.json index c3029e46d..eae391f37 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,9 @@ "eslint:fix": "eslint ./src ./scripts ./bin --fix", "lint": "pnpm run prettier:check && pnpm run eslint:check && pnpm run cspell:check && pnpm run typescript:check", "lint:fix": "pnpm run prettier:fix && pnpm run eslint:fix && pnpm run cspell:check && pnpm run typescript:check", - "prepublishOnly": "rm -rf dist; tsc --declaration --emitDeclarationOnly --noEmit false -p tsconfig.json --outDir dist; rm -rf dist/scripts; mv dist/src/* dist; rm -rf dist/src; cp src/*.d.ts dist", - "postpublish": "rm -rf dist; npm run build", + "postinstall": "tsx scripts/build-bin.mts", + "prepublishOnly": "rm -rf dist; tsc --declaration --emitDeclarationOnly --noEmit false -p tsconfig.json --outDir dist; rm -rf dist/scripts; mv dist/src/* dist; rm -rf dist/src; cp src/*.d.ts dist; tsx scripts/create-import-wrappers.mts", + "postpublish": "rm -rf *.d.ts; rm -rf dist; npm run build", "docs": "typedoc src/renderer/replugged.ts --excludeExternals", "docs:watch": "pnpm run docs --watch" }, @@ -59,7 +60,6 @@ "eslint-plugin-react": "^7.33.2", "moment": "^2.29.4", "np": "^8.0.4", - "parcel": "^2.9.3", "prettier": "^3.0.3", "simple-markdown": "^0.7.3", "style-mod": "^4.1.0", @@ -70,7 +70,8 @@ }, "files": [ "dist", - "bin" + "bin", + "*.d.ts" ], "dependencies": { "@codemirror/lang-css": "^6.2.1", @@ -80,13 +81,12 @@ "@electron/asar": "^3.2.4", "@lezer/highlight": "^1.1.6", "@octokit/rest": "^20.0.1", - "@parcel/config-default": "^2.9.3", - "@parcel/core": "^2.9.3", - "@parcel/transformer-sass": "^2.9.3", "adm-zip": "^0.5.10", "chalk": "^5.3.0", "codemirror": "^6.0.1", - "esbuild": "^0.16.17", + "esbuild": "^0.19.2", + "esbuild-sass-plugin": "^2.13.0", + "esm": "^3.2.25", "node-fetch": "^3.3.2", "prompts": "^2.4.2", "semver": "^7.5.4", @@ -97,7 +97,7 @@ "zod": "^3.22.2" }, "bin": { - "replugged": "bin/replugged.mjs" + "replugged": "bin.mjs" }, "pnpm": { "overrides": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db6dce499..c1e2e7fcc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,15 +29,6 @@ dependencies: '@octokit/rest': specifier: ^20.0.1 version: 20.0.1 - '@parcel/config-default': - specifier: ^2.9.3 - version: 2.9.3(@parcel/core@2.9.3)(typescript@5.2.2) - '@parcel/core': - specifier: ^2.9.3 - version: 2.9.3 - '@parcel/transformer-sass': - specifier: ^2.9.3 - version: 2.9.3(@parcel/core@2.9.3) adm-zip: specifier: ^0.5.10 version: 0.5.10 @@ -48,8 +39,14 @@ dependencies: specifier: ^6.0.1 version: 6.0.1(@lezer/common@1.0.4) esbuild: - specifier: ^0.16.17 - version: 0.16.17 + specifier: ^0.19.2 + version: 0.19.4 + esbuild-sass-plugin: + specifier: ^2.13.0 + version: 2.16.0(esbuild@0.19.4) + esm: + specifier: ^3.2.25 + version: 3.2.25 node-fetch: specifier: ^3.3.2 version: 3.3.2 @@ -145,9 +142,6 @@ devDependencies: np: specifier: ^8.0.4 version: 8.0.4(typescript@5.2.2) - parcel: - specifier: ^2.9.3 - version: 2.9.3(typescript@5.2.2) prettier: specifier: ^3.0.3 version: 3.0.3 @@ -183,10 +177,12 @@ packages: dependencies: '@babel/highlight': 7.22.13 chalk: 2.4.2 + dev: true /@babel/helper-validator-identifier@7.22.15: resolution: {integrity: sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==} engines: {node: '>=6.9.0'} + dev: true /@babel/highlight@7.22.13: resolution: {integrity: sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==} @@ -195,6 +191,7 @@ packages: '@babel/helper-validator-identifier': 7.22.15 chalk: 2.4.2 js-tokens: 4.0.0 + dev: true /@bconnorwhite/module@2.0.2: resolution: {integrity: sha512-ck1me5WMgZKp06gnJrVKEkytpehTTQbvsAMbF1nGPeHri/AZNhj87++PSE2LOxmZqM0EtGMaqeLdx7Lw7SUnTA==} @@ -607,15 +604,6 @@ packages: get-tsconfig: 4.7.0 dev: true - /@esbuild/android-arm64@0.16.17: - resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: false - optional: true - /@esbuild/android-arm64@0.18.20: resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} engines: {node: '>=12'} @@ -625,10 +613,10 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.16.17: - resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} + /@esbuild/android-arm64@0.19.4: + resolution: {integrity: sha512-mRsi2vJsk4Bx/AFsNBqOH2fqedxn5L/moT58xgg51DjX1la64Z3Npicut2VbhvDFO26qjWtPMsVxCd80YTFVeg==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [android] requiresBuild: true dev: false @@ -643,10 +631,10 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.16.17: - resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} + /@esbuild/android-arm@0.19.4: + resolution: {integrity: sha512-uBIbiYMeSsy2U0XQoOGVVcpIktjLMEKa7ryz2RLr7L/vTnANNEsPVAh4xOv7ondGz6ac1zVb0F8Jx20rQikffQ==} engines: {node: '>=12'} - cpu: [x64] + cpu: [arm] os: [android] requiresBuild: true dev: false @@ -661,11 +649,11 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.16.17: - resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} + /@esbuild/android-x64@0.19.4: + resolution: {integrity: sha512-4iPufZ1TMOD3oBlGFqHXBpa3KFT46aLl6Vy7gwed0ZSYgHaZ/mihbYb4t7Z9etjkC9Al3ZYIoOaHrU60gcMy7g==} engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] + cpu: [x64] + os: [android] requiresBuild: true dev: false optional: true @@ -679,10 +667,10 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.16.17: - resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} + /@esbuild/darwin-arm64@0.19.4: + resolution: {integrity: sha512-Lviw8EzxsVQKpbS+rSt6/6zjn9ashUZ7Tbuvc2YENgRl0yZTktGlachZ9KMJUsVjZEGFVu336kl5lBgDN6PmpA==} engines: {node: '>=12'} - cpu: [x64] + cpu: [arm64] os: [darwin] requiresBuild: true dev: false @@ -697,11 +685,11 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.16.17: - resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} + /@esbuild/darwin-x64@0.19.4: + resolution: {integrity: sha512-YHbSFlLgDwglFn0lAO3Zsdrife9jcQXQhgRp77YiTDja23FrC2uwnhXMNkAucthsf+Psr7sTwYEryxz6FPAVqw==} engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] + cpu: [x64] + os: [darwin] requiresBuild: true dev: false optional: true @@ -715,10 +703,10 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.16.17: - resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} + /@esbuild/freebsd-arm64@0.19.4: + resolution: {integrity: sha512-vz59ijyrTG22Hshaj620e5yhs2dU1WJy723ofc+KUgxVCM6zxQESmWdMuVmUzxtGqtj5heHyB44PjV/HKsEmuQ==} engines: {node: '>=12'} - cpu: [x64] + cpu: [arm64] os: [freebsd] requiresBuild: true dev: false @@ -733,11 +721,11 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.16.17: - resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} + /@esbuild/freebsd-x64@0.19.4: + resolution: {integrity: sha512-3sRbQ6W5kAiVQRBWREGJNd1YE7OgzS0AmOGjDmX/qZZecq8NFlQsQH0IfXjjmD0XtUYqr64e0EKNFjMUlPL3Cw==} engines: {node: '>=12'} - cpu: [arm64] - os: [linux] + cpu: [x64] + os: [freebsd] requiresBuild: true dev: false optional: true @@ -751,10 +739,10 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.16.17: - resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} + /@esbuild/linux-arm64@0.19.4: + resolution: {integrity: sha512-ZWmWORaPbsPwmyu7eIEATFlaqm0QGt+joRE9sKcnVUG3oBbr/KYdNE2TnkzdQwX6EDRdg/x8Q4EZQTXoClUqqA==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [linux] requiresBuild: true dev: false @@ -769,10 +757,10 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.16.17: - resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} + /@esbuild/linux-arm@0.19.4: + resolution: {integrity: sha512-z/4ArqOo9EImzTi4b6Vq+pthLnepFzJ92BnofU1jgNlcVb+UqynVFdoXMCFreTK7FdhqAzH0vmdwW5373Hm9pg==} engines: {node: '>=12'} - cpu: [ia32] + cpu: [arm] os: [linux] requiresBuild: true dev: false @@ -787,10 +775,10 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.16.17: - resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} + /@esbuild/linux-ia32@0.19.4: + resolution: {integrity: sha512-EGc4vYM7i1GRUIMqRZNCTzJh25MHePYsnQfKDexD8uPTCm9mK56NIL04LUfX2aaJ+C9vyEp2fJ7jbqFEYgO9lQ==} engines: {node: '>=12'} - cpu: [loong64] + cpu: [ia32] os: [linux] requiresBuild: true dev: false @@ -805,10 +793,10 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.16.17: - resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} + /@esbuild/linux-loong64@0.19.4: + resolution: {integrity: sha512-WVhIKO26kmm8lPmNrUikxSpXcgd6HDog0cx12BUfA2PkmURHSgx9G6vA19lrlQOMw+UjMZ+l3PpbtzffCxFDRg==} engines: {node: '>=12'} - cpu: [mips64el] + cpu: [loong64] os: [linux] requiresBuild: true dev: false @@ -823,10 +811,10 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.16.17: - resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} + /@esbuild/linux-mips64el@0.19.4: + resolution: {integrity: sha512-keYY+Hlj5w86hNp5JJPuZNbvW4jql7c1eXdBUHIJGTeN/+0QFutU3GrS+c27L+NTmzi73yhtojHk+lr2+502Mw==} engines: {node: '>=12'} - cpu: [ppc64] + cpu: [mips64el] os: [linux] requiresBuild: true dev: false @@ -841,10 +829,10 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.16.17: - resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} + /@esbuild/linux-ppc64@0.19.4: + resolution: {integrity: sha512-tQ92n0WMXyEsCH4m32S21fND8VxNiVazUbU4IUGVXQpWiaAxOBvtOtbEt3cXIV3GEBydYsY8pyeRMJx9kn3rvw==} engines: {node: '>=12'} - cpu: [riscv64] + cpu: [ppc64] os: [linux] requiresBuild: true dev: false @@ -859,10 +847,10 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.16.17: - resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} + /@esbuild/linux-riscv64@0.19.4: + resolution: {integrity: sha512-tRRBey6fG9tqGH6V75xH3lFPpj9E8BH+N+zjSUCnFOX93kEzqS0WdyJHkta/mmJHn7MBaa++9P4ARiU4ykjhig==} engines: {node: '>=12'} - cpu: [s390x] + cpu: [riscv64] os: [linux] requiresBuild: true dev: false @@ -877,10 +865,10 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.16.17: - resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} + /@esbuild/linux-s390x@0.19.4: + resolution: {integrity: sha512-152aLpQqKZYhThiJ+uAM4PcuLCAOxDsCekIbnGzPKVBRUDlgaaAfaUl5NYkB1hgY6WN4sPkejxKlANgVcGl9Qg==} engines: {node: '>=12'} - cpu: [x64] + cpu: [s390x] os: [linux] requiresBuild: true dev: false @@ -895,11 +883,11 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.16.17: - resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} + /@esbuild/linux-x64@0.19.4: + resolution: {integrity: sha512-Mi4aNA3rz1BNFtB7aGadMD0MavmzuuXNTaYL6/uiYIs08U7YMPETpgNn5oue3ICr+inKwItOwSsJDYkrE9ekVg==} engines: {node: '>=12'} cpu: [x64] - os: [netbsd] + os: [linux] requiresBuild: true dev: false optional: true @@ -913,11 +901,11 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.16.17: - resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} + /@esbuild/netbsd-x64@0.19.4: + resolution: {integrity: sha512-9+Wxx1i5N/CYo505CTT7T+ix4lVzEdz0uCoYGxM5JDVlP2YdDC1Bdz+Khv6IbqmisT0Si928eAxbmGkcbiuM/A==} engines: {node: '>=12'} cpu: [x64] - os: [openbsd] + os: [netbsd] requiresBuild: true dev: false optional: true @@ -931,11 +919,11 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.16.17: - resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} + /@esbuild/openbsd-x64@0.19.4: + resolution: {integrity: sha512-MFsHleM5/rWRW9EivFssop+OulYVUoVcqkyOkjiynKBCGBj9Lihl7kh9IzrreDyXa4sNkquei5/DTP4uCk25xw==} engines: {node: '>=12'} cpu: [x64] - os: [sunos] + os: [openbsd] requiresBuild: true dev: false optional: true @@ -949,11 +937,11 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.16.17: - resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} + /@esbuild/sunos-x64@0.19.4: + resolution: {integrity: sha512-6Xq8SpK46yLvrGxjp6HftkDwPP49puU4OF0hEL4dTxqCbfx09LyrbUj/D7tmIRMj5D5FCUPksBbxyQhp8tmHzw==} engines: {node: '>=12'} - cpu: [arm64] - os: [win32] + cpu: [x64] + os: [sunos] requiresBuild: true dev: false optional: true @@ -967,10 +955,10 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.16.17: - resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} + /@esbuild/win32-arm64@0.19.4: + resolution: {integrity: sha512-PkIl7Jq4mP6ke7QKwyg4fD4Xvn8PXisagV/+HntWoDEdmerB2LTukRZg728Yd1Fj+LuEX75t/hKXE2Ppk8Hh1w==} engines: {node: '>=12'} - cpu: [ia32] + cpu: [arm64] os: [win32] requiresBuild: true dev: false @@ -985,10 +973,10 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.16.17: - resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} + /@esbuild/win32-ia32@0.19.4: + resolution: {integrity: sha512-ga676Hnvw7/ycdKB53qPusvsKdwrWzEyJ+AtItHGoARszIqvjffTwaaW3b2L6l90i7MO9i+dlAW415INuRhSGg==} engines: {node: '>=12'} - cpu: [x64] + cpu: [ia32] os: [win32] requiresBuild: true dev: false @@ -1003,6 +991,15 @@ packages: dev: true optional: true + /@esbuild/win32-x64@0.19.4: + resolution: {integrity: sha512-HP0GDNla1T3ZL8Ko/SHAS2GgtjOg+VmWnnYLhuTksr++EnduYB0f3Y2LzHsUwb2iQ13JGoY6G3R8h6Du/WG6uA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.48.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1060,9 +1057,6 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true - /@lezer/common@0.15.12: - resolution: {integrity: sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==} - /@lezer/common@1.0.4: resolution: {integrity: sha512-lZHlk8p67x4aIDtJl6UQrXSOP6oi7dQR3W/geFVrENdA1JDaAJWldnVqVjPMJupbTKbzDfFcePfKttqVidS/dg==} dev: false @@ -1080,11 +1074,6 @@ packages: '@lezer/common': 1.0.4 dev: false - /@lezer/lr@0.15.8: - resolution: {integrity: sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==} - dependencies: - '@lezer/common': 0.15.12 - /@lezer/lr@1.3.10: resolution: {integrity: sha512-BZfVvf7Re5BIwJHlZXbJn9L8lus5EonxQghyn+ih8Wl36XMFBPTXC0KM0IdUtj9w/diPHsKlXVgL+AlX2jYJ0Q==} dependencies: @@ -1096,98 +1085,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /@lmdb/lmdb-darwin-arm64@2.7.11: - resolution: {integrity: sha512-r6+vYq2vKzE+vgj/rNVRMwAevq0+ZR9IeMFIqcSga+wMtMdXQ27KqQ7uS99/yXASg29bos7yHP3yk4x6Iio0lw==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - optional: true - - /@lmdb/lmdb-darwin-x64@2.7.11: - resolution: {integrity: sha512-jhj1aB4K8ycRL1HOQT5OtzlqOq70jxUQEWRN9Gqh3TIDN30dxXtiHi6EWF516tzw6v2+3QqhDMJh8O6DtTGG8Q==} - cpu: [x64] - os: [darwin] - requiresBuild: true - optional: true - - /@lmdb/lmdb-linux-arm64@2.7.11: - resolution: {integrity: sha512-7xGEfPPbmVJWcY2Nzqo11B9Nfxs+BAsiiaY/OcT4aaTDdykKeCjvKMQJA3KXCtZ1AtiC9ljyGLi+BfUwdulY5A==} - cpu: [arm64] - os: [linux] - requiresBuild: true - optional: true - - /@lmdb/lmdb-linux-arm@2.7.11: - resolution: {integrity: sha512-dHfLFVSrw/v5X5lkwp0Vl7+NFpEeEYKfMG2DpdFJnnG1RgHQZngZxCaBagFoaJGykRpd2DYF1AeuXBFrAUAXfw==} - cpu: [arm] - os: [linux] - requiresBuild: true - optional: true - - /@lmdb/lmdb-linux-x64@2.7.11: - resolution: {integrity: sha512-vUKI3JrREMQsXX8q0Eq5zX2FlYCKWMmLiCyyJNfZK0Uyf14RBg9VtB3ObQ41b4swYh2EWaltasWVe93Y8+KDng==} - cpu: [x64] - os: [linux] - requiresBuild: true - optional: true - - /@lmdb/lmdb-win32-x64@2.7.11: - resolution: {integrity: sha512-BJwkHlSUgtB+Ei52Ai32M1AOMerSlzyIGA/KC4dAGL+GGwVMdwG8HGCOA2TxP3KjhbgDPMYkv7bt/NmOmRIFng==} - cpu: [x64] - os: [win32] - requiresBuild: true - optional: true - - /@mischnic/json-sourcemap@0.1.0: - resolution: {integrity: sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==} - engines: {node: '>=12.0.0'} - dependencies: - '@lezer/common': 0.15.12 - '@lezer/lr': 0.15.8 - json5: 2.2.3 - - /@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2: - resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - optional: true - - /@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2: - resolution: {integrity: sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==} - cpu: [x64] - os: [darwin] - requiresBuild: true - optional: true - - /@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2: - resolution: {integrity: sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==} - cpu: [arm64] - os: [linux] - requiresBuild: true - optional: true - - /@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2: - resolution: {integrity: sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==} - cpu: [arm] - os: [linux] - requiresBuild: true - optional: true - - /@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2: - resolution: {integrity: sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==} - cpu: [x64] - os: [linux] - requiresBuild: true - optional: true - - /@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2: - resolution: {integrity: sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==} - cpu: [x64] - os: [win32] - requiresBuild: true - optional: true - /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1314,764 +1211,6 @@ packages: '@octokit/openapi-types': 18.0.0 dev: false - /@parcel/bundler-default@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-JjJK8dq39/UO/MWI/4SCbB1t/qgpQRFnFDetAAAezQ8oN++b24u1fkMDa/xqQGjbuPmGeTds5zxGgYs7id7PYg==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/graph': 2.9.3 - '@parcel/hash': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/cache@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-Bj/H2uAJJSXtysG7E/x4EgTrE2hXmm7td/bc97K8M9N7+vQjxf7xb0ebgqe84ePVMkj4MVQSMEJkEucXVx4b0Q==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@parcel/core': ^2.9.3 - dependencies: - '@parcel/core': 2.9.3 - '@parcel/fs': 2.9.3(@parcel/core@2.9.3) - '@parcel/logger': 2.9.3 - '@parcel/utils': 2.9.3 - lmdb: 2.7.11 - - /@parcel/codeframe@2.9.3: - resolution: {integrity: sha512-z7yTyD6h3dvduaFoHpNqur74/2yDWL++33rjQjIjCaXREBN6dKHoMGMizzo/i4vbiI1p9dDox2FIDEHCMQxqdA==} - engines: {node: '>= 12.0.0'} - dependencies: - chalk: 4.1.2 - - /@parcel/compressor-raw@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-jz3t4/ICMsHEqgiTmv5i1DJva2k5QRpZlBELVxfY+QElJTVe8edKJ0TiKcBxh2hx7sm4aUigGmp7JiqqHRRYmA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/config-default@2.9.3(@parcel/core@2.9.3)(typescript@5.2.2): - resolution: {integrity: sha512-tqN5tF7QnVABDZAu76co5E6N8mA9n8bxiWdK4xYyINYFIEHgX172oRTqXTnhEMjlMrdmASxvnGlbaPBaVnrCTw==} - peerDependencies: - '@parcel/core': ^2.9.3 - dependencies: - '@parcel/bundler-default': 2.9.3(@parcel/core@2.9.3) - '@parcel/compressor-raw': 2.9.3(@parcel/core@2.9.3) - '@parcel/core': 2.9.3 - '@parcel/namer-default': 2.9.3(@parcel/core@2.9.3) - '@parcel/optimizer-css': 2.9.3(@parcel/core@2.9.3) - '@parcel/optimizer-htmlnano': 2.9.3(@parcel/core@2.9.3)(typescript@5.2.2) - '@parcel/optimizer-image': 2.9.3(@parcel/core@2.9.3) - '@parcel/optimizer-svgo': 2.9.3(@parcel/core@2.9.3) - '@parcel/optimizer-swc': 2.9.3(@parcel/core@2.9.3) - '@parcel/packager-css': 2.9.3(@parcel/core@2.9.3) - '@parcel/packager-html': 2.9.3(@parcel/core@2.9.3) - '@parcel/packager-js': 2.9.3(@parcel/core@2.9.3) - '@parcel/packager-raw': 2.9.3(@parcel/core@2.9.3) - '@parcel/packager-svg': 2.9.3(@parcel/core@2.9.3) - '@parcel/reporter-dev-server': 2.9.3(@parcel/core@2.9.3) - '@parcel/resolver-default': 2.9.3(@parcel/core@2.9.3) - '@parcel/runtime-browser-hmr': 2.9.3(@parcel/core@2.9.3) - '@parcel/runtime-js': 2.9.3(@parcel/core@2.9.3) - '@parcel/runtime-react-refresh': 2.9.3(@parcel/core@2.9.3) - '@parcel/runtime-service-worker': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-babel': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-css': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-html': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-image': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-js': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-json': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-postcss': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-posthtml': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-raw': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-react-refresh-wrap': 2.9.3(@parcel/core@2.9.3) - '@parcel/transformer-svg': 2.9.3(@parcel/core@2.9.3) - transitivePeerDependencies: - - '@swc/helpers' - - cssnano - - postcss - - purgecss - - relateurl - - srcset - - terser - - typescript - - uncss - - /@parcel/core@2.9.3: - resolution: {integrity: sha512-4KlM1Zr/jpsqWuMXr2zmGsaOUs1zMMFh9vfCNKRZkptf+uk8I3sugHbNdo+F5B+4e2yMuOEb1zgAmvJLeuH6ww==} - engines: {node: '>= 12.0.0'} - dependencies: - '@mischnic/json-sourcemap': 0.1.0 - '@parcel/cache': 2.9.3(@parcel/core@2.9.3) - '@parcel/diagnostic': 2.9.3 - '@parcel/events': 2.9.3 - '@parcel/fs': 2.9.3(@parcel/core@2.9.3) - '@parcel/graph': 2.9.3 - '@parcel/hash': 2.9.3 - '@parcel/logger': 2.9.3 - '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/profiler': 2.9.3 - '@parcel/source-map': 2.1.1 - '@parcel/types': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - '@parcel/workers': 2.9.3(@parcel/core@2.9.3) - abortcontroller-polyfill: 1.7.5 - base-x: 3.0.9 - browserslist: 4.21.10 - clone: 2.1.2 - dotenv: 7.0.0 - dotenv-expand: 5.1.0 - json5: 2.2.3 - msgpackr: 1.9.8 - nullthrows: 1.1.1 - semver: 7.5.4 - - /@parcel/diagnostic@2.9.3: - resolution: {integrity: sha512-6jxBdyB3D7gP4iE66ghUGntWt2v64E6EbD4AetZk+hNJpgudOOPsKTovcMi/i7I4V0qD7WXSF4tvkZUoac0jwA==} - engines: {node: '>= 12.0.0'} - dependencies: - '@mischnic/json-sourcemap': 0.1.0 - nullthrows: 1.1.1 - - /@parcel/events@2.9.3: - resolution: {integrity: sha512-K0Scx+Bx9f9p1vuShMzNwIgiaZUkxEnexaKYHYemJrM7pMAqxIuIqhnvwurRCsZOVLUJPDDNJ626cWTc5vIq+A==} - engines: {node: '>= 12.0.0'} - - /@parcel/fs-search@2.9.3: - resolution: {integrity: sha512-nsNz3bsOpwS+jphcd+XjZL3F3PDq9lik0O8HPm5f6LYkqKWT+u/kgQzA8OkAHCR3q96LGiHxUywHPEBc27vI4Q==} - engines: {node: '>= 12.0.0'} - - /@parcel/fs@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-/PrRKgCRw22G7rNPSpgN3Q+i2nIkZWuvIOAdMG4KWXC4XLp8C9jarNaWd5QEQ75amjhQSl3oUzABzkdCtkKrgg==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@parcel/core': ^2.9.3 - dependencies: - '@parcel/core': 2.9.3 - '@parcel/fs-search': 2.9.3 - '@parcel/types': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - '@parcel/watcher': 2.3.0 - '@parcel/workers': 2.9.3(@parcel/core@2.9.3) - - /@parcel/graph@2.9.3: - resolution: {integrity: sha512-3LmRJmF8+OprAr6zJT3X2s8WAhLKkrhi6RsFlMWHifGU5ED1PFcJWFbOwJvSjcAhMQJP0fErcFIK1Ludv3Vm3g==} - engines: {node: '>= 12.0.0'} - dependencies: - nullthrows: 1.1.1 - - /@parcel/hash@2.9.3: - resolution: {integrity: sha512-qlH5B85XLzVAeijgKPjm1gQu35LoRYX/8igsjnN8vOlbc3O8BYAUIutU58fbHbtE8MJPbxQQUw7tkTjeoujcQQ==} - engines: {node: '>= 12.0.0'} - dependencies: - xxhash-wasm: 0.4.2 - - /@parcel/logger@2.9.3: - resolution: {integrity: sha512-5FNBszcV6ilGFcijEOvoNVG6IUJGsnMiaEnGQs7Fvc1dktTjEddnoQbIYhcSZL63wEmzBZOgkT5yDMajJ/41jw==} - engines: {node: '>= 12.0.0'} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/events': 2.9.3 - - /@parcel/markdown-ansi@2.9.3: - resolution: {integrity: sha512-/Q4X8F2aN8UNjAJrQ5NfK2OmZf6shry9DqetUSEndQ0fHonk78WKt6LT0zSKEBEW/bB/bXk6mNMsCup6L8ibjQ==} - engines: {node: '>= 12.0.0'} - dependencies: - chalk: 4.1.2 - - /@parcel/namer-default@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-1ynFEcap48/Ngzwwn318eLYpLUwijuuZoXQPCsEQ21OOIOtfhFQJaPwXTsw6kRitshKq76P2aafE0BioGSqxcA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/node-resolver-core@3.0.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-AjxNcZVHHJoNT/A99PKIdFtwvoze8PAiC3yz8E/dRggrDIOboUEodeQYV5Aq++aK76uz/iOP0tST2T8A5rhb1A==} - engines: {node: '>= 12.0.0'} - dependencies: - '@mischnic/json-sourcemap': 0.1.0 - '@parcel/diagnostic': 2.9.3 - '@parcel/fs': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - nullthrows: 1.1.1 - semver: 7.5.4 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/optimizer-css@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-RK1QwcSdWDNUsFvuLy0hgnYKtPQebzCb0vPPzqs6LhL+vqUu9utOyRycGaQffHCkHVQP6zGlN+KFssd7YtFGhA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/source-map': 2.1.1 - '@parcel/utils': 2.9.3 - browserslist: 4.21.10 - lightningcss: 1.21.7 - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/optimizer-htmlnano@2.9.3(@parcel/core@2.9.3)(typescript@5.2.2): - resolution: {integrity: sha512-9g/KBck3c6DokmJfvJ5zpHFBiCSolaGrcsTGx8C3YPdCTVTI9P1TDCwUxvAr4LjpcIRSa82wlLCI+nF6sSgxKA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - htmlnano: 2.0.4(svgo@2.8.0)(typescript@5.2.2) - nullthrows: 1.1.1 - posthtml: 0.16.6 - svgo: 2.8.0 - transitivePeerDependencies: - - '@parcel/core' - - cssnano - - postcss - - purgecss - - relateurl - - srcset - - terser - - typescript - - uncss - - /@parcel/optimizer-image@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-530YzthE7kmecnNhPbkAK+26yQNt69pfJrgE0Ev0BZaM1Wu2+33nki7o8qvkTkikhPrurEJLGIXt1qKmbKvCbA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - peerDependencies: - '@parcel/core': ^2.9.3 - dependencies: - '@parcel/core': 2.9.3 - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - '@parcel/workers': 2.9.3(@parcel/core@2.9.3) - - /@parcel/optimizer-svgo@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-ytQS0wY5JJhWU4mL0wfhYDUuHcfuw+Gy2+JcnTm1t1AZXHlOTbU6EzRWNqBShsgXjvdrQQXizAe3B6GFFlFJVQ==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - svgo: 2.8.0 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/optimizer-swc@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-GQINNeqtdpL1ombq/Cpwi6IBk02wKJ/JJbYbyfHtk8lxlq13soenpwOlzJ5T9D2fdG+FUhai9NxpN5Ss4lNoAg==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/source-map': 2.1.1 - '@parcel/utils': 2.9.3 - '@swc/core': 1.3.83 - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - - '@swc/helpers' - - /@parcel/package-manager@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-NH6omcNTEupDmW4Lm1e4NUYBjdqkURxgZ4CNESESInHJe6tblVhNB8Rpr1ar7zDar7cly9ILr8P6N3Ei7bTEjg==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@parcel/core': ^2.9.3 - dependencies: - '@parcel/core': 2.9.3 - '@parcel/diagnostic': 2.9.3 - '@parcel/fs': 2.9.3(@parcel/core@2.9.3) - '@parcel/logger': 2.9.3 - '@parcel/node-resolver-core': 3.0.3(@parcel/core@2.9.3) - '@parcel/types': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - '@parcel/workers': 2.9.3(@parcel/core@2.9.3) - semver: 7.5.4 - - /@parcel/packager-css@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-mePiWiYZOULY6e1RdAIJyRoYqXqGci0srOaVZYaP7mnrzvJgA63kaZFFsDiEWghunQpMUuUjM2x/vQVHzxmhKQ==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/source-map': 2.1.1 - '@parcel/utils': 2.9.3 - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/packager-html@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-0Ex+O0EaZf9APNERRNGgGto02hFJ6f5RQEvRWBK55WAV1rXeU+kpjC0c0qZvnUaUtXfpWMsEBkevJCwDkUMeMg==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/types': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - nullthrows: 1.1.1 - posthtml: 0.16.6 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/packager-js@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-V5xwkoE3zQ3R+WqAWhA1KGQ791FvJeW6KonOlMI1q76Djjgox68hhObqcLu66AmYNhR2R/wUpkP18hP2z8dSFw==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/hash': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/source-map': 2.1.1 - '@parcel/utils': 2.9.3 - globals: 13.21.0 - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/packager-raw@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-oPQTNoYanQ2DdJyL61uPYK2py83rKOT8YVh2QWAx0zsSli6Kiy64U3+xOCYWgDVCrHw9+9NpQMuAdSiFg4cq8g==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/packager-svg@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-p/Ya6UO9DAkaCUFxfFGyeHZDp9YPAlpdnh1OChuwqSFOXFjjeXuoK4KLT+ZRalVBo2Jo8xF70oKMZw4MVvaL7Q==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/types': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - posthtml: 0.16.6 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/plugin@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-qN85Gqr2GMuxX1dT1mnuO9hOcvlEv1lrYrCxn7CJN2nUhbwcfG+LEvcrCzCOJ6XtIHm+ZBV9h9p7FfoPLvpw+g==} - engines: {node: '>= 12.0.0'} - dependencies: - '@parcel/types': 2.9.3(@parcel/core@2.9.3) - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/profiler@2.9.3: - resolution: {integrity: sha512-pyHc9lw8VZDfgZoeZWZU9J0CVEv1Zw9O5+e0DJPDPHuXJYr72ZAOhbljtU3owWKAeW+++Q2AZWkbUGEOjI/e6g==} - engines: {node: '>= 12.0.0'} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/events': 2.9.3 - chrome-trace-event: 1.0.3 - - /@parcel/reporter-cli@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-pZiEvQpuXFuQBafMHxkDmwH8CnnK9sWHwa3bSbsnt385aUahtE8dpY0LKt+K1zfB6degKoczN6aWVj9WycQuZQ==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/types': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - chalk: 4.1.2 - term-size: 2.2.1 - transitivePeerDependencies: - - '@parcel/core' - dev: true - - /@parcel/reporter-dev-server@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-s6eboxdLEtRSvG52xi9IiNbcPKC0XMVmvTckieue2EqGDbDcaHQoHmmwkk0rNq0/Z/UxelGcQXoIYC/0xq3ykQ==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/reporter-tracer@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-9cXpKWk0m6d6d+4+TlAdOe8XIPaFEIKGWMWG+5SFAQE08u3olet4PSvd49F4+ZZo5ftRE7YI3j6xNbXvJT8KGw==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - chrome-trace-event: 1.0.3 - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - dev: true - - /@parcel/resolver-default@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-8ESJk1COKvDzkmOnppNXoDamNMlYVIvrKc2RuFPmp8nKVj47R6NwMgvwxEaatyPzvkmyTpq5RvG9I3HFc+r4Cw==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/node-resolver-core': 3.0.3(@parcel/core@2.9.3) - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/runtime-browser-hmr@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-EgiDIDrVAWpz7bOzWXqVinQkaFjLwT34wsonpXAbuI7f7r00d52vNAQC9AMu+pTijA3gyKoJ+Q4NWPMZf7ACDA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/runtime-js@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-EvIy+qXcKnB5qxHhe96zmJpSAViNVXHfQI5RSdZ2a7CPwORwhTI+zPNT9sb7xb/WwFw/WuTTgzT40b41DceU6Q==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/runtime-react-refresh@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-XBgryZQIyCmi6JwEfMUCmINB3l1TpTp9a2iFxmYNpzHlqj4Ve0saKaqWOVRLvC945ZovWIBzcSW2IYqWKGtbAA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - react-error-overlay: 6.0.9 - react-refresh: 0.9.0 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/runtime-service-worker@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-qLJLqv1mMdWL7gyh8aKBFFAuEiJkhUUgLKpdn6eSfH/R7kTtb76WnOwqUrhvEI9bZFUM/8Pa1bzJnPpqSOM+Sw==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/source-map@2.1.1: - resolution: {integrity: sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==} - engines: {node: ^12.18.3 || >=14} - dependencies: - detect-libc: 1.0.3 - - /@parcel/transformer-babel@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-pURtEsnsp3h6tOBDuzh9wRvVtw4PgIlqwAArIWdrG7iwqOUYv9D8ME4+ePWEu7MQWAp58hv9pTJtqWv4T+Sq8A==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/source-map': 2.1.1 - '@parcel/utils': 2.9.3 - browserslist: 4.21.10 - json5: 2.2.3 - nullthrows: 1.1.1 - semver: 7.5.4 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/transformer-css@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-duWMdbEBBPjg3fQdXF16iWIdThetDZvCs2TpUD7xOlXH6kR0V5BJy8ONFT15u1RCqIV9hSNGaS3v3I9YRNY5zQ==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/source-map': 2.1.1 - '@parcel/utils': 2.9.3 - browserslist: 4.21.10 - lightningcss: 1.21.7 - nullthrows: 1.1.1 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/transformer-html@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-0NU4omcHzFXA1seqftAXA2KNZaMByoKaNdXnLgBgtCGDiYvOcL+6xGHgY6pw9LvOh5um10KI5TxSIMILoI7VtA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/hash': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - nullthrows: 1.1.1 - posthtml: 0.16.6 - posthtml-parser: 0.10.2 - posthtml-render: 3.0.0 - semver: 7.5.4 - srcset: 4.0.0 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/transformer-image@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-7CEe35RaPadQzLIuxzTtIxnItvOoy46hcbXtOdDt6lmVa4omuOygZYRIya2lsGIP4JHvAaALMb5nt99a1uTwJg==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - peerDependencies: - '@parcel/core': ^2.9.3 - dependencies: - '@parcel/core': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - '@parcel/workers': 2.9.3(@parcel/core@2.9.3) - nullthrows: 1.1.1 - - /@parcel/transformer-js@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-Z2MVVg5FYcPOfxlUwxqb5l9yjTMEqE3KI3zq2MBRUme6AV07KxLmCDF23b6glzZlHWQUE8MXzYCTAkOPCcPz+Q==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - peerDependencies: - '@parcel/core': ^2.9.3 - dependencies: - '@parcel/core': 2.9.3 - '@parcel/diagnostic': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/source-map': 2.1.1 - '@parcel/utils': 2.9.3 - '@parcel/workers': 2.9.3(@parcel/core@2.9.3) - '@swc/helpers': 0.5.2 - browserslist: 4.21.10 - nullthrows: 1.1.1 - regenerator-runtime: 0.13.11 - semver: 7.5.4 - - /@parcel/transformer-json@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-yNL27dbOLhkkrjaQjiQ7Im9VOxmkfuuSNSmS0rA3gEjVcm07SLKRzWkAaPnyx44Lb6bzyOTWwVrb9aMmxgADpA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - json5: 2.2.3 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/transformer-postcss@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-HoDvPqKzhpmvMmHqQhDnt8F1vH61m6plpGiYaYnYv2Om4HHi5ZIq9bO+9QLBnTKfaZ7ndYSefTKOxTYElg7wyw==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/hash': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - clone: 2.1.2 - nullthrows: 1.1.1 - postcss-value-parser: 4.2.0 - semver: 7.5.4 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/transformer-posthtml@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-2fQGgrzRmaqbWf3y2/T6xhqrNjzqMMKksqJzvc8TMfK6f2kg3Ddjv158eaSW2JdkV39aY7tvAOn5f1uzo74BMA==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - nullthrows: 1.1.1 - posthtml: 0.16.6 - posthtml-parser: 0.10.2 - posthtml-render: 3.0.0 - semver: 7.5.4 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/transformer-raw@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-oqdPzMC9QzWRbY9J6TZEqltknjno+dY24QWqf8ondmdF2+W+/2mRDu59hhCzQrqUHgTq4FewowRZmSfpzHxwaQ==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/transformer-react-refresh-wrap@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-cb9NyU6oJlDblFIlzqIE8AkvRQVGl2IwJNKwD4PdE7Y6sq2okGEPG4hOw3k/Y9JVjM4/2pUORqvjSRhWwd9oVQ==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - react-refresh: 0.9.0 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/transformer-sass@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-i9abj9bKg3xCHghJyTM3rUVxIEn9n1Rl+DFdpyNAD8VZ52COfOshFDQOWNuhU1hEnJOFYCjnfcO0HRTsg3dWmg==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - '@parcel/source-map': 2.1.1 - sass: 1.66.1 - transitivePeerDependencies: - - '@parcel/core' - dev: false - - /@parcel/transformer-svg@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-ypmE+dzB09IMCdEAkOsSxq1dEIm2A3h67nAFz4qbfHbwNgXBUuy/jB3ZMwXN/cO0f7SBh/Ap8Jhq6vmGqB5tWw==} - engines: {node: '>= 12.0.0', parcel: ^2.9.3} - dependencies: - '@parcel/diagnostic': 2.9.3 - '@parcel/hash': 2.9.3 - '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) - nullthrows: 1.1.1 - posthtml: 0.16.6 - posthtml-parser: 0.10.2 - posthtml-render: 3.0.0 - semver: 7.5.4 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/types@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-NSNY8sYtRhvF1SqhnIGgGvJocyWt1K8Tnw5cVepm0g38ywtX6mwkBvMkmeehXkII4mSUn+frD9wGsydTunezvA==} - dependencies: - '@parcel/cache': 2.9.3(@parcel/core@2.9.3) - '@parcel/diagnostic': 2.9.3 - '@parcel/fs': 2.9.3(@parcel/core@2.9.3) - '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) - '@parcel/source-map': 2.1.1 - '@parcel/workers': 2.9.3(@parcel/core@2.9.3) - utility-types: 3.10.0 - transitivePeerDependencies: - - '@parcel/core' - - /@parcel/utils@2.9.3: - resolution: {integrity: sha512-cesanjtj/oLehW8Waq9JFPmAImhoiHX03ihc3JTWkrvJYSbD7wYKCDgPAM3JiRAqvh1LZ6P699uITrYWNoRLUg==} - engines: {node: '>= 12.0.0'} - dependencies: - '@parcel/codeframe': 2.9.3 - '@parcel/diagnostic': 2.9.3 - '@parcel/hash': 2.9.3 - '@parcel/logger': 2.9.3 - '@parcel/markdown-ansi': 2.9.3 - '@parcel/source-map': 2.1.1 - chalk: 4.1.2 - nullthrows: 1.1.1 - - /@parcel/watcher-android-arm64@2.3.0: - resolution: {integrity: sha512-f4o9eA3dgk0XRT3XhB0UWpWpLnKgrh1IwNJKJ7UJek7eTYccQ8LR7XUWFKqw6aEq5KUNlCcGvSzKqSX/vtWVVA==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [android] - requiresBuild: true - optional: true - - /@parcel/watcher-darwin-arm64@2.3.0: - resolution: {integrity: sha512-mKY+oijI4ahBMc/GygVGvEdOq0L4DxhYgwQqYAz/7yPzuGi79oXrZG52WdpGA1wLBPrYb0T8uBaGFo7I6rvSKw==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - optional: true - - /@parcel/watcher-darwin-x64@2.3.0: - resolution: {integrity: sha512-20oBj8LcEOnLE3mgpy6zuOq8AplPu9NcSSSfyVKgfOhNAc4eF4ob3ldj0xWjGGbOF7Dcy1Tvm6ytvgdjlfUeow==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [darwin] - requiresBuild: true - optional: true - - /@parcel/watcher-freebsd-x64@2.3.0: - resolution: {integrity: sha512-7LftKlaHunueAEiojhCn+Ef2CTXWsLgTl4hq0pkhkTBFI3ssj2bJXmH2L67mKpiAD5dz66JYk4zS66qzdnIOgw==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - optional: true - - /@parcel/watcher-linux-arm-glibc@2.3.0: - resolution: {integrity: sha512-1apPw5cD2xBv1XIHPUlq0cO6iAaEUQ3BcY0ysSyD9Kuyw4MoWm1DV+W9mneWI+1g6OeP6dhikiFE6BlU+AToTQ==} - engines: {node: '>= 10.0.0'} - cpu: [arm] - os: [linux] - requiresBuild: true - optional: true - - /@parcel/watcher-linux-arm64-glibc@2.3.0: - resolution: {integrity: sha512-mQ0gBSQEiq1k/MMkgcSB0Ic47UORZBmWoAWlMrTW6nbAGoLZP+h7AtUM7H3oDu34TBFFvjy4JCGP43JlylkTQA==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - optional: true - - /@parcel/watcher-linux-arm64-musl@2.3.0: - resolution: {integrity: sha512-LXZAExpepJew0Gp8ZkJ+xDZaTQjLHv48h0p0Vw2VMFQ8A+RKrAvpFuPVCVwKJCr5SE+zvaG+Etg56qXvTDIedw==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - optional: true - - /@parcel/watcher-linux-x64-glibc@2.3.0: - resolution: {integrity: sha512-P7Wo91lKSeSgMTtG7CnBS6WrA5otr1K7shhSjKHNePVmfBHDoAOHYRXgUmhiNfbcGk0uMCHVcdbfxtuiZCHVow==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - optional: true - - /@parcel/watcher-linux-x64-musl@2.3.0: - resolution: {integrity: sha512-+kiRE1JIq8QdxzwoYY+wzBs9YbJ34guBweTK8nlzLKimn5EQ2b2FSC+tAOpq302BuIMjyuUGvBiUhEcLIGMQ5g==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - optional: true - - /@parcel/watcher-win32-arm64@2.3.0: - resolution: {integrity: sha512-35gXCnaz1AqIXpG42evcoP2+sNL62gZTMZne3IackM+6QlfMcJLy3DrjuL6Iks7Czpd3j4xRBzez3ADCj1l7Aw==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [win32] - requiresBuild: true - optional: true - - /@parcel/watcher-win32-ia32@2.3.0: - resolution: {integrity: sha512-FJS/IBQHhRpZ6PiCjFt1UAcPr0YmCLHRbTc00IBTrelEjlmmgIVLeOx4MSXzx2HFEy5Jo5YdhGpxCuqCyDJ5ow==} - engines: {node: '>= 10.0.0'} - cpu: [ia32] - os: [win32] - requiresBuild: true - optional: true - - /@parcel/watcher-win32-x64@2.3.0: - resolution: {integrity: sha512-dLx+0XRdMnVI62kU3wbXvbIRhLck4aE28bIGKbRGS7BJNt54IIj9+c/Dkqb+7DJEbHUZAX1bwaoM8PqVlHJmCA==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [win32] - requiresBuild: true - optional: true - - /@parcel/watcher@2.3.0: - resolution: {integrity: sha512-pW7QaFiL11O0BphO+bq3MgqeX/INAk9jgBldVDYjlQPO4VddoZnF22TcF9onMhnLVHuNqBJeRf+Fj7eezi/+rQ==} - engines: {node: '>= 10.0.0'} - requiresBuild: true - dependencies: - detect-libc: 1.0.3 - is-glob: 4.0.3 - micromatch: 4.0.5 - node-addon-api: 7.0.0 - optionalDependencies: - '@parcel/watcher-android-arm64': 2.3.0 - '@parcel/watcher-darwin-arm64': 2.3.0 - '@parcel/watcher-darwin-x64': 2.3.0 - '@parcel/watcher-freebsd-x64': 2.3.0 - '@parcel/watcher-linux-arm-glibc': 2.3.0 - '@parcel/watcher-linux-arm64-glibc': 2.3.0 - '@parcel/watcher-linux-arm64-musl': 2.3.0 - '@parcel/watcher-linux-x64-glibc': 2.3.0 - '@parcel/watcher-linux-x64-musl': 2.3.0 - '@parcel/watcher-win32-arm64': 2.3.0 - '@parcel/watcher-win32-ia32': 2.3.0 - '@parcel/watcher-win32-x64': 2.3.0 - - /@parcel/workers@2.9.3(@parcel/core@2.9.3): - resolution: {integrity: sha512-zRrDuZJzTevrrwElYosFztgldhqW6G9q5zOeQXfVQFkkEJCNfg36ixeiofKRU8uu2x+j+T6216mhMNB6HiuY+w==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@parcel/core': ^2.9.3 - dependencies: - '@parcel/core': 2.9.3 - '@parcel/diagnostic': 2.9.3 - '@parcel/logger': 2.9.3 - '@parcel/profiler': 2.9.3 - '@parcel/types': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - nullthrows: 1.1.1 - /@pnpm/config.env-replace@1.1.0: resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} @@ -2113,120 +1252,9 @@ packages: engines: {node: '>=10'} dev: true - /@sindresorhus/is@5.6.0: - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} - - /@swc/core-darwin-arm64@1.3.83: - resolution: {integrity: sha512-Plz2IKeveVLivbXTSCC3OZjD2MojyKYllhPrn9RotkDIZEFRYJZtW5/Ik1tJW/2rzu5HVKuGYrDKdScVVTbOxQ==} - engines: {node: '>=10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - optional: true - - /@swc/core-darwin-x64@1.3.83: - resolution: {integrity: sha512-FBGVg5IPF/8jQ6FbK60iDUHjv0H5+LwfpJHKH6wZnRaYWFtm7+pzYgreLu3NTsm3m7/1a7t0+7KURwBGUaJCCw==} - engines: {node: '>=10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - optional: true - - /@swc/core-linux-arm-gnueabihf@1.3.83: - resolution: {integrity: sha512-EZcsuRYhGkzofXtzwDjuuBC/suiX9s7zeg2YYXOVjWwyebb6BUhB1yad3mcykFQ20rTLO9JUyIaiaMYDHGobqw==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux] - requiresBuild: true - optional: true - - /@swc/core-linux-arm64-gnu@1.3.83: - resolution: {integrity: sha512-khI41szLHrCD/cFOcN4p2SYvZgHjhhHlcMHz5BksRrDyteSJKu0qtWRZITVom0N/9jWoAleoFhMnFTUs0H8IWA==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - optional: true - - /@swc/core-linux-arm64-musl@1.3.83: - resolution: {integrity: sha512-zgT7yNOdbjHcGAwvys79mbfNLK65KBlPJWzeig+Yk7I8TVzmaQge7B6ZS/gwF9/p+8TiLYo/tZ5aF2lqlgdSVw==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - optional: true - - /@swc/core-linux-x64-gnu@1.3.83: - resolution: {integrity: sha512-x+mH0Y3NC/G0YNlFmGi3vGD4VOm7IPDhh+tGrx6WtJp0BsShAbOpxtfU885rp1QweZe4qYoEmGqiEjE2WrPIdA==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - requiresBuild: true - optional: true - - /@swc/core-linux-x64-musl@1.3.83: - resolution: {integrity: sha512-s5AYhAOmetUwUZwS5g9qb92IYgNHHBGiY2mTLImtEgpAeBwe0LPDj6WrujxCBuZnaS55mKRLLOuiMZE5TpjBNA==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - requiresBuild: true - optional: true - - /@swc/core-win32-arm64-msvc@1.3.83: - resolution: {integrity: sha512-yw2rd/KVOGs95lRRB+killLWNaO1dy4uVa8Q3/4wb5txlLru07W1m041fZLzwOg/1Sh0TMjJgGxj0XHGR3ZXhQ==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - optional: true - - /@swc/core-win32-ia32-msvc@1.3.83: - resolution: {integrity: sha512-POW+rgZ6KWqBpwPGIRd2/3pcf46P+UrKBm4HLt5IwbHvekJ4avIM8ixJa9kK0muJNVJcDpaZgxaU1ELxtJ1j8w==} - engines: {node: '>=10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - optional: true - - /@swc/core-win32-x64-msvc@1.3.83: - resolution: {integrity: sha512-CiWQtkFnZElXQUalaHp+Wacw0Jd+24ncRYhqaJ9YKnEQP1H82CxIIuQqLM8IFaLpn5dpY6SgzaeubWF46hjcLA==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - requiresBuild: true - optional: true - - /@swc/core@1.3.83: - resolution: {integrity: sha512-PccHDgGQlFjpExgJxH91qA3a4aifR+axCFJ4RieCoiI0m5gURE4nBhxzTBY5YU/YKTBmPO8Gc5Q6inE3+NquWg==} - engines: {node: '>=10'} - requiresBuild: true - peerDependencies: - '@swc/helpers': ^0.5.0 - peerDependenciesMeta: - '@swc/helpers': - optional: true - dependencies: - '@swc/types': 0.1.4 - optionalDependencies: - '@swc/core-darwin-arm64': 1.3.83 - '@swc/core-darwin-x64': 1.3.83 - '@swc/core-linux-arm-gnueabihf': 1.3.83 - '@swc/core-linux-arm64-gnu': 1.3.83 - '@swc/core-linux-arm64-musl': 1.3.83 - '@swc/core-linux-x64-gnu': 1.3.83 - '@swc/core-linux-x64-musl': 1.3.83 - '@swc/core-win32-arm64-msvc': 1.3.83 - '@swc/core-win32-ia32-msvc': 1.3.83 - '@swc/core-win32-x64-msvc': 1.3.83 - - /@swc/helpers@0.5.2: - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} - dependencies: - tslib: 2.6.2 - - /@swc/types@0.1.4: - resolution: {integrity: sha512-z/G02d+59gyyUb7KYhKi9jOhicek6QD2oMaotUyG+lUkybpXoV49dY9bj7Ah5Q+y7knK2jU67UTX9FyfGzaxQg==} + /@sindresorhus/is@5.6.0: + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} /@szmarczak/http-timer@4.0.6: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} @@ -2241,10 +1269,6 @@ packages: dependencies: defer-to-connect: 2.0.1 - /@trysound/sax@0.2.0: - resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} - engines: {node: '>=10.13.0'} - /@types/adm-zip@0.5.0: resolution: {integrity: sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==} dependencies: @@ -2509,9 +1533,6 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /abortcontroller-polyfill@1.7.5: - resolution: {integrity: sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==} - /acorn-jsx@5.3.2(acorn@8.10.0): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2620,6 +1641,7 @@ packages: engines: {node: '>=4'} dependencies: color-convert: 1.9.3 + dev: true /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} @@ -2656,6 +1678,7 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true /array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -2741,11 +1764,6 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - /base-x@3.0.9: - resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} - dependencies: - safe-buffer: 5.2.1 - /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true @@ -2772,9 +1790,6 @@ packages: readable-stream: 3.6.2 dev: true - /boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - /boxen@7.1.1: resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} engines: {node: '>=14.16'} @@ -2813,16 +1828,6 @@ packages: dependencies: fill-range: 7.0.1 - /browserslist@4.21.10: - resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001528 - electron-to-chromium: 1.4.511 - node-releases: 2.0.13 - update-browserslist-db: 1.0.11(browserslist@4.21.10) - /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true @@ -2889,6 +1894,7 @@ packages: /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + dev: true /callsites@4.1.0: resolution: {integrity: sha512-aBMbD1Xxay75ViYezwT40aQONfr+pSXTHwNKvIXhXD6+LY3F1dLIcceoC5OZKBVHbXcysz1hL9D2w0JJIMXpUw==} @@ -2899,9 +1905,6 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} - /caniuse-lite@1.0.30001528: - resolution: {integrity: sha512-0Db4yyjR9QMNlsxh+kKWzQtkyflkG/snYheSzkjmvdEtEXB1+jt7A2HmSEiO6XIJPIbo92lHNGNySvE5pZcs5Q==} - /chalk-template@1.1.0: resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==} engines: {node: '>=14.16'} @@ -2927,6 +1930,7 @@ packages: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 + dev: true /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -2934,6 +1938,7 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + dev: true /chalk@5.3.0: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} @@ -2958,10 +1963,6 @@ packages: fsevents: 2.3.3 dev: false - /chrome-trace-event@1.0.3: - resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} - engines: {node: '>=6.0'} - /chromium-pickle-js@0.2.0: resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==} dev: false @@ -3050,10 +2051,6 @@ packages: engines: {node: '>=0.8'} dev: true - /clone@2.1.2: - resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} - engines: {node: '>=0.8'} - /code-point-at@1.1.0: resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} engines: {node: '>=0.10.0'} @@ -3077,6 +2074,7 @@ packages: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 + dev: true /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -3086,6 +2084,7 @@ packages: /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -3112,10 +2111,6 @@ packages: engines: {node: '>= 6'} dev: true - /commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - /comment-json@4.2.3: resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==} engines: {node: '>= 6'} @@ -3174,6 +2169,7 @@ packages: parse-json: 5.2.0 path-type: 4.0.0 typescript: 5.2.2 + dev: true /crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} @@ -3306,32 +2302,6 @@ packages: - encoding dev: true - /css-select@4.3.0: - resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} - dependencies: - boolbase: 1.0.0 - css-what: 6.1.0 - domhandler: 4.3.1 - domutils: 2.8.0 - nth-check: 2.1.1 - - /css-tree@1.1.3: - resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} - engines: {node: '>=8.0.0'} - dependencies: - mdn-data: 2.0.14 - source-map: 0.6.1 - - /css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} - engines: {node: '>= 6'} - - /csso@4.2.0: - resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} - engines: {node: '>=8.0.0'} - dependencies: - css-tree: 1.1.3 - /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} dev: true @@ -3430,11 +2400,6 @@ packages: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} dev: false - /detect-libc@1.0.3: - resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} - engines: {node: '>=0.10'} - hasBin: true - /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -3463,29 +2428,6 @@ packages: esutils: 2.0.3 dev: true - /dom-serializer@1.4.1: - resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} - dependencies: - domelementtype: 2.3.0 - domhandler: 4.3.1 - entities: 2.2.0 - - /domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - /domhandler@4.3.1: - resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} - engines: {node: '>= 4'} - dependencies: - domelementtype: 2.3.0 - - /domutils@2.8.0: - resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} - dependencies: - dom-serializer: 1.4.1 - domelementtype: 2.3.0 - domhandler: 4.3.1 - /dot-prop@6.0.1: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} @@ -3499,19 +2441,9 @@ packages: type-fest: 2.19.0 dev: true - /dotenv-expand@5.1.0: - resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} - - /dotenv@7.0.0: - resolution: {integrity: sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==} - engines: {node: '>=6'} - /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - /electron-to-chromium@1.4.511: - resolution: {integrity: sha512-udHyLfdy390CObLy3uFQitCBvK+WxWu6WZWQMBzO/npNiRy6tanDKR1c/F6OImfAiSt1ylgNszPJBxix2c0w3w==} - /elegant-spinner@1.0.1: resolution: {integrity: sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==} engines: {node: '>=0.10.0'} @@ -3529,17 +2461,11 @@ packages: once: 1.4.0 dev: true - /entities@2.2.0: - resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - - /entities@3.0.1: - resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} - engines: {node: '>=0.12'} - /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: is-arrayish: 0.2.1 + dev: true /es-abstract@1.22.1: resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} @@ -3629,34 +2555,14 @@ packages: is-symbol: 1.0.4 dev: true - /esbuild@0.16.17: - resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/android-arm': 0.16.17 - '@esbuild/android-arm64': 0.16.17 - '@esbuild/android-x64': 0.16.17 - '@esbuild/darwin-arm64': 0.16.17 - '@esbuild/darwin-x64': 0.16.17 - '@esbuild/freebsd-arm64': 0.16.17 - '@esbuild/freebsd-x64': 0.16.17 - '@esbuild/linux-arm': 0.16.17 - '@esbuild/linux-arm64': 0.16.17 - '@esbuild/linux-ia32': 0.16.17 - '@esbuild/linux-loong64': 0.16.17 - '@esbuild/linux-mips64el': 0.16.17 - '@esbuild/linux-ppc64': 0.16.17 - '@esbuild/linux-riscv64': 0.16.17 - '@esbuild/linux-s390x': 0.16.17 - '@esbuild/linux-x64': 0.16.17 - '@esbuild/netbsd-x64': 0.16.17 - '@esbuild/openbsd-x64': 0.16.17 - '@esbuild/sunos-x64': 0.16.17 - '@esbuild/win32-arm64': 0.16.17 - '@esbuild/win32-ia32': 0.16.17 - '@esbuild/win32-x64': 0.16.17 + /esbuild-sass-plugin@2.16.0(esbuild@0.19.4): + resolution: {integrity: sha512-mGCe9MxNYvZ+j77Q/QFO+rwUGA36mojDXkOhtVmoyz1zwYbMaNrtVrmXwwYDleS/UMKTNU3kXuiTtPiAD3K+Pw==} + peerDependencies: + esbuild: ^0.19.4 + dependencies: + esbuild: 0.19.4 + resolve: 1.22.8 + sass: 1.66.1 dev: false /esbuild@0.18.20: @@ -3689,9 +2595,40 @@ packages: '@esbuild/win32-x64': 0.18.20 dev: true + /esbuild@0.19.4: + resolution: {integrity: sha512-x7jL0tbRRpv4QUyuDMjONtWFciygUxWaUM1kMX2zWxI0X2YWOt7MSA0g4UdeSiHM8fcYVzpQhKYOycZwxTdZkA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.19.4 + '@esbuild/android-arm64': 0.19.4 + '@esbuild/android-x64': 0.19.4 + '@esbuild/darwin-arm64': 0.19.4 + '@esbuild/darwin-x64': 0.19.4 + '@esbuild/freebsd-arm64': 0.19.4 + '@esbuild/freebsd-x64': 0.19.4 + '@esbuild/linux-arm': 0.19.4 + '@esbuild/linux-arm64': 0.19.4 + '@esbuild/linux-ia32': 0.19.4 + '@esbuild/linux-loong64': 0.19.4 + '@esbuild/linux-mips64el': 0.19.4 + '@esbuild/linux-ppc64': 0.19.4 + '@esbuild/linux-riscv64': 0.19.4 + '@esbuild/linux-s390x': 0.19.4 + '@esbuild/linux-x64': 0.19.4 + '@esbuild/netbsd-x64': 0.19.4 + '@esbuild/openbsd-x64': 0.19.4 + '@esbuild/sunos-x64': 0.19.4 + '@esbuild/win32-arm64': 0.19.4 + '@esbuild/win32-ia32': 0.19.4 + '@esbuild/win32-x64': 0.19.4 + dev: false + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} + dev: false /escape-goat@4.0.0: resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} @@ -3700,6 +2637,7 @@ packages: /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} + dev: true /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -3833,6 +2771,11 @@ packages: - supports-color dev: true + /esm@3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + dev: false + /espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4071,7 +3014,6 @@ packages: /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: true /function.prototype.name@1.1.6: resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} @@ -4106,11 +3048,6 @@ packages: has-symbols: 1.0.3 dev: true - /get-port@4.2.0: - resolution: {integrity: sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==} - engines: {node: '>=6'} - dev: true - /get-stdin@9.0.0: resolution: {integrity: sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==} engines: {node: '>=12'} @@ -4179,6 +3116,7 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.20.2 + dev: true /globalthis@1.0.3: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} @@ -4273,10 +3211,12 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} + dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + dev: true /has-own-prop@2.0.0: resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} @@ -4315,7 +3255,6 @@ packages: engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 - dev: true /hosted-git-info@4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} @@ -4331,50 +3270,6 @@ packages: lru-cache: 7.18.3 dev: true - /htmlnano@2.0.4(svgo@2.8.0)(typescript@5.2.2): - resolution: {integrity: sha512-WGCkyGFwjKW1GeCBsPYacMvaMnZtFJ0zIRnC2NCddkA+IOEhTqskXrS7lep+3yYZw/nQ3dW1UAX4yA/GJyR8BA==} - peerDependencies: - cssnano: ^6.0.0 - postcss: ^8.3.11 - purgecss: ^5.0.0 - relateurl: ^0.2.7 - srcset: 4.0.0 - svgo: ^3.0.2 - terser: ^5.10.0 - uncss: ^0.17.3 - peerDependenciesMeta: - cssnano: - optional: true - postcss: - optional: true - purgecss: - optional: true - relateurl: - optional: true - srcset: - optional: true - svgo: - optional: true - terser: - optional: true - uncss: - optional: true - dependencies: - cosmiconfig: 8.3.4(typescript@5.2.2) - posthtml: 0.16.6 - svgo: 2.8.0 - timsort: 0.3.0 - transitivePeerDependencies: - - typescript - - /htmlparser2@7.2.0: - resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} - dependencies: - domelementtype: 2.3.0 - domhandler: 4.3.1 - domutils: 2.8.0 - entities: 3.0.1 - /http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} @@ -4436,6 +3331,7 @@ packages: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 + dev: true /import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} @@ -4570,6 +3466,7 @@ packages: /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true /is-async-function@2.0.0: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} @@ -4614,7 +3511,6 @@ packages: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: has: 1.0.3 - dev: true /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} @@ -4699,9 +3595,6 @@ packages: engines: {node: '>=12'} dev: true - /is-json@2.0.1: - resolution: {integrity: sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==} - /is-map@2.0.2: resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} dev: true @@ -4899,18 +3792,21 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true dependencies: argparse: 2.0.1 + dev: true /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -4920,11 +3816,6 @@ packages: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - /jsonc-parser@3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true @@ -4962,96 +3853,9 @@ packages: type-check: 0.4.0 dev: true - /lightningcss-darwin-arm64@1.21.7: - resolution: {integrity: sha512-tt7hIsFio9jZofTVHtCACz6rB6c9RyABMXfA9A/VcKOjS3sq+koX/QkRJWY06utwOImbJIXBC5hbg9t3RkPUAQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - optional: true - - /lightningcss-darwin-x64@1.21.7: - resolution: {integrity: sha512-F4gS4bf7eWekfPT+TxJNm/pF+QRgZiTrTkQH6cw4/UWfdeZISfuhD5El2dm16giFnY0K5ylIwO+ZusgYNkGSXA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - requiresBuild: true - optional: true - - /lightningcss-freebsd-x64@1.21.7: - resolution: {integrity: sha512-RMfNzJWXCSfPnL55fcLWEAadcY6QUFT0S8NceNKYzp1KiCZtkJIy6RQ5SaVxPzRqd3iMsahUf5sfnG8N1UQSNQ==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - optional: true - - /lightningcss-linux-arm-gnueabihf@1.21.7: - resolution: {integrity: sha512-biSRUDZNx7vubWP1jArw/qqfZKPGpkV/qzunasZzxmqijbZ43sW9faDQYxWNcxPWljJJdF/qs6qcurYFovWtrQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - requiresBuild: true - optional: true - - /lightningcss-linux-arm64-gnu@1.21.7: - resolution: {integrity: sha512-PENY8QekqL9TG3AY/A7rkUBb5ymefGxea7Oe7+x7Hbw4Bz4Hpj5cec5OoMypMqFbURPmpi0fTWx4vSWUPzpDcA==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - optional: true - - /lightningcss-linux-arm64-musl@1.21.7: - resolution: {integrity: sha512-pfOipKvA/0X1OjRaZt3870vnV9UGBSjayIqHh0fGx/+aRz3O0MVFHE/60P2UWXpM3YGJEw/hMWtNkrFwqOge8A==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - optional: true - - /lightningcss-linux-x64-gnu@1.21.7: - resolution: {integrity: sha512-dgcsis4TAA7s0ia4f31QHX+G4PWPwxk+wJaEQLaV0NdJs09O5hHoA8DpLEr8nrvc/tsRTyVNBP1rDtgzySjpXg==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - optional: true - - /lightningcss-linux-x64-musl@1.21.7: - resolution: {integrity: sha512-A+9dXpxld3p4Cd6fxev2eqEvaauYtrgNpXV3t7ioCJy30Oj9nYiNGwiGusM+4MJVcEpUPGUGiuAqY4sWilRDwA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - optional: true - - /lightningcss-win32-x64-msvc@1.21.7: - resolution: {integrity: sha512-07/8vogEq+C/mF99pdMhh/f19/xreq8N9Ca6AWeVHZIdODyF/pt6KdKSCWDZWIn+3CUxI8gCJWuUWyOc3xymvw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - requiresBuild: true - optional: true - - /lightningcss@1.21.7: - resolution: {integrity: sha512-xITZyh5sLFwRPYUSw15T00Rm7gcQ1qOPuQwNOcvHsTm6nLWTQ723w7zl42wrC5t+xtdg6FPmnXHml1nZxxvp1w==} - engines: {node: '>= 12.0.0'} - dependencies: - detect-libc: 1.0.3 - optionalDependencies: - lightningcss-darwin-arm64: 1.21.7 - lightningcss-darwin-x64: 1.21.7 - lightningcss-freebsd-x64: 1.21.7 - lightningcss-linux-arm-gnueabihf: 1.21.7 - lightningcss-linux-arm64-gnu: 1.21.7 - lightningcss-linux-arm64-musl: 1.21.7 - lightningcss-linux-x64-gnu: 1.21.7 - lightningcss-linux-x64-musl: 1.21.7 - lightningcss-win32-x64-msvc: 1.21.7 - /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true /listr-input@0.2.1: resolution: {integrity: sha512-oa8iVG870qJq+OuuMK3DjGqFcwsK1SDu+kULp9kEq09TY231aideIZenr3lFOQdASpAr6asuyJBbX62/a3IIhg==} @@ -5113,24 +3917,6 @@ packages: - zenObservable dev: true - /lmdb@2.7.11: - resolution: {integrity: sha512-x9bD4hVp7PFLUoELL8RglbNXhAMt5CYhkmss+CEau9KlNoilsTzNi9QDsPZb3KMpOGZXG6jmXhW3bBxE2XVztw==} - hasBin: true - requiresBuild: true - dependencies: - msgpackr: 1.8.5 - node-addon-api: 4.3.0 - node-gyp-build-optional-packages: 5.0.6 - ordered-binary: 1.4.1 - weak-lru-cache: 1.2.2 - optionalDependencies: - '@lmdb/lmdb-darwin-arm64': 2.7.11 - '@lmdb/lmdb-darwin-x64': 2.7.11 - '@lmdb/lmdb-linux-arm': 2.7.11 - '@lmdb/lmdb-linux-arm64': 2.7.11 - '@lmdb/lmdb-linux-x64': 2.7.11 - '@lmdb/lmdb-win32-x64': 2.7.11 - /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -5237,9 +4023,6 @@ packages: hasBin: true dev: true - /mdn-data@2.0.14: - resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} - /meow@12.1.1: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} @@ -5260,6 +4043,7 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 + dev: true /mimic-fn@1.2.0: resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} @@ -5312,31 +4096,6 @@ packages: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true - /msgpackr-extract@3.0.2: - resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==} - hasBin: true - requiresBuild: true - dependencies: - node-gyp-build-optional-packages: 5.0.7 - optionalDependencies: - '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.2 - '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.2 - '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.2 - '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.2 - '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.2 - '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.2 - optional: true - - /msgpackr@1.8.5: - resolution: {integrity: sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg==} - optionalDependencies: - msgpackr-extract: 3.0.2 - - /msgpackr@1.9.8: - resolution: {integrity: sha512-dQvfSMSIQ9kXXQTlJFDq+f7J3RrmydhI6Tn23lFy7BItp7zDR3nH70CHk2QIfs2copLSaKRv/PPjMbNSTFu2hA==} - optionalDependencies: - msgpackr-extract: 3.0.2 - /mute-stream@0.0.7: resolution: {integrity: sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==} dev: true @@ -5361,12 +4120,6 @@ packages: type-fest: 2.19.0 dev: true - /node-addon-api@4.3.0: - resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} - - /node-addon-api@7.0.0: - resolution: {integrity: sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==} - /node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -5393,19 +4146,6 @@ packages: formdata-polyfill: 4.0.10 dev: false - /node-gyp-build-optional-packages@5.0.6: - resolution: {integrity: sha512-2ZJErHG4du9G3/8IWl/l9Bp5BBFy63rno5GVmjQijvTuUZKsl6g8RB4KH/x3NLcV5ZBb4GsXmAuTYr6dRml3Gw==} - hasBin: true - - /node-gyp-build-optional-packages@5.0.7: - resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} - hasBin: true - requiresBuild: true - optional: true - - /node-releases@2.0.13: - resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} - /normalize-package-data@3.0.3: resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} engines: {node: '>=10'} @@ -5507,14 +4247,6 @@ packages: path-key: 4.0.0 dev: true - /nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - dependencies: - boolbase: 1.0.0 - - /nullthrows@1.1.1: - resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} - /number-is-nan@1.0.1: resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} engines: {node: '>=0.10.0'} @@ -5641,9 +4373,6 @@ packages: wcwidth: 1.0.1 dev: true - /ordered-binary@1.4.1: - resolution: {integrity: sha512-9LtiGlPy982CsgxZvJGNNp2/NnrgEr6EAyN3iIEP3/8vd3YLgAZQHbQ75ZrkfBRGrNg37Dk3U6tuVb+B4Xfslg==} - /org-regex@1.0.0: resolution: {integrity: sha512-7bqkxkEJwzJQUAlyYniqEZ3Ilzjh0yoa62c7gL6Ijxj5bEpPL+8IE1Z0PFj0ywjjXQcdrwR51g9MIcLezR0hKQ==} engines: {node: '>=8'} @@ -5763,45 +4492,12 @@ packages: resolution: {integrity: sha512-DPBNWSUWC0wPofXeNThao0uP4a93J7r90UyhagmJS0QcacTTkorZwXYsOop70phn1hKdcf/2e9lJIhazS8bx5A==} dev: true - /parcel@2.9.3(typescript@5.2.2): - resolution: {integrity: sha512-2GTVocFkwblV/TIg9AmT7TI2fO4xdWkyN8aFUEVtiVNWt96GTR3FgQyHFValfCbcj1k9Xf962Ws2hYXYUr9k1Q==} - engines: {node: '>= 12.0.0'} - hasBin: true - peerDependenciesMeta: - '@parcel/core': - optional: true - dependencies: - '@parcel/config-default': 2.9.3(@parcel/core@2.9.3)(typescript@5.2.2) - '@parcel/core': 2.9.3 - '@parcel/diagnostic': 2.9.3 - '@parcel/events': 2.9.3 - '@parcel/fs': 2.9.3(@parcel/core@2.9.3) - '@parcel/logger': 2.9.3 - '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) - '@parcel/reporter-cli': 2.9.3(@parcel/core@2.9.3) - '@parcel/reporter-dev-server': 2.9.3(@parcel/core@2.9.3) - '@parcel/reporter-tracer': 2.9.3(@parcel/core@2.9.3) - '@parcel/utils': 2.9.3 - chalk: 4.1.2 - commander: 7.2.0 - get-port: 4.2.0 - transitivePeerDependencies: - - '@swc/helpers' - - cssnano - - postcss - - purgecss - - relateurl - - srcset - - terser - - typescript - - uncss - dev: true - /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} dependencies: callsites: 3.1.0 + dev: true /parent-module@2.0.0: resolution: {integrity: sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==} @@ -5830,6 +4526,7 @@ packages: error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + dev: true /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -5857,14 +4554,11 @@ packages: /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -5884,34 +4578,6 @@ packages: find-up: 6.3.0 dev: true - /postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - - /posthtml-parser@0.10.2: - resolution: {integrity: sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==} - engines: {node: '>=12'} - dependencies: - htmlparser2: 7.2.0 - - /posthtml-parser@0.11.0: - resolution: {integrity: sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==} - engines: {node: '>=12'} - dependencies: - htmlparser2: 7.2.0 - - /posthtml-render@3.0.0: - resolution: {integrity: sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==} - engines: {node: '>=12'} - dependencies: - is-json: 2.0.1 - - /posthtml@0.16.6: - resolution: {integrity: sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==} - engines: {node: '>=12.0.0'} - dependencies: - posthtml-parser: 0.11.0 - posthtml-render: 3.0.0 - /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -5982,17 +4648,10 @@ packages: minimist: 1.2.8 strip-json-comments: 2.0.1 - /react-error-overlay@6.0.9: - resolution: {integrity: sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==} - /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true - /react-refresh@0.9.0: - resolution: {integrity: sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==} - engines: {node: '>=0.10.0'} - /read-file-safe@1.0.10: resolution: {integrity: sha512-qW25fd2uMX3dV6Ui/R0jYK1MhTpjx8FO/VHaHTXzwWsGnkNwLRcqYfCXd9qDM+NZ273DPUvP2RaimYuLSu1K/g==} dev: true @@ -6051,9 +4710,6 @@ packages: which-builtin-type: 1.1.3 dev: true - /regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} - /regexp.prototype.flags@1.5.0: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} engines: {node: '>= 0.4'} @@ -6110,6 +4766,7 @@ packages: /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + dev: true /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} @@ -6129,6 +4786,15 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: false + /resolve@2.0.0-next.4: resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} hasBin: true @@ -6226,6 +4892,7 @@ packages: /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} @@ -6339,6 +5006,7 @@ packages: /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + dev: true /spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -6362,14 +5030,6 @@ packages: resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} dev: true - /srcset@4.0.0: - resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} - engines: {node: '>=12'} - - /stable@0.1.8: - resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} - deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' - /standalone-electron-types@1.0.0: resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==} dependencies: @@ -6518,12 +5178,14 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 + dev: true /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 + dev: true /supports-hyperlinks@2.3.0: resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} @@ -6536,20 +5198,6 @@ packages: /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - dev: true - - /svgo@2.8.0: - resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} - engines: {node: '>=10.13.0'} - hasBin: true - dependencies: - '@trysound/sax': 0.2.0 - commander: 7.2.0 - css-select: 4.3.0 - css-tree: 1.1.3 - csso: 4.2.0 - picocolors: 1.0.0 - stable: 0.1.8 /symbol-observable@1.2.0: resolution: {integrity: sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==} @@ -6561,11 +5209,6 @@ packages: engines: {node: '>=0.10'} dev: true - /term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - dev: true - /terminal-link@3.0.0: resolution: {integrity: sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==} engines: {node: '>=12'} @@ -6582,9 +5225,6 @@ packages: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} dev: true - /timsort@0.3.0: - resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} - /titleize@3.0.0: resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} engines: {node: '>=12'} @@ -6622,6 +5262,7 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true /tsx@3.12.8: resolution: {integrity: sha512-Lt9KYaRGF023tlLInPj8rgHwsZU8qWLBj4iRXNWxTfjIkU7canGL806AqKear1j722plHuiYNcL2ZCo6uS9UJA==} @@ -6644,6 +5285,7 @@ packages: /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + dev: true /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} @@ -6746,6 +5388,7 @@ packages: resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} engines: {node: '>=14.17'} hasBin: true + dev: true /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -6771,16 +5414,6 @@ packages: engines: {node: '>=8'} dev: true - /update-browserslist-db@1.0.11(browserslist@4.21.10): - resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.10 - escalade: 3.1.1 - picocolors: 1.0.0 - /update-notifier@6.0.2: resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} engines: {node: '>=14.16'} @@ -6810,10 +5443,6 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /utility-types@3.10.0: - resolution: {integrity: sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==} - engines: {node: '>= 4'} - /vali-date@1.0.0: resolution: {integrity: sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==} engines: {node: '>=0.10.0'} @@ -6858,9 +5487,6 @@ packages: defaults: 1.0.4 dev: true - /weak-lru-cache@1.2.2: - resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} - /web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} engines: {node: '>= 8'} @@ -7001,9 +5627,6 @@ packages: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} - /xxhash-wasm@0.4.2: - resolution: {integrity: sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==} - /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} diff --git a/scripts/build-bin.mts b/scripts/build-bin.mts new file mode 100644 index 000000000..3c31be376 --- /dev/null +++ b/scripts/build-bin.mts @@ -0,0 +1,28 @@ +import esbuild from "esbuild"; +import { readFileSync } from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const NODE_VERSION = "14"; + +const dirname = path.dirname(fileURLToPath(import.meta.url)); + +const packageJson = JSON.parse(readFileSync(path.join(dirname, "..", "package.json"), "utf-8")); +const packageNames = Object.keys(packageJson.dependencies); + +const context = await esbuild.context({ + absWorkingDir: path.join(dirname, ".."), + bundle: true, + minify: true, + format: "esm", + logLevel: "info", + metafile: true, + entryPoints: ["bin/index.mts"], + platform: "node", + target: `node${NODE_VERSION}`, + outfile: "bin.mjs", + external: packageNames, +}); + +await context.rebuild(); +context.dispose(); diff --git a/scripts/build.mts b/scripts/build.mts index a4389fa46..a879f74c0 100644 --- a/scripts/build.mts +++ b/scripts/build.mts @@ -11,6 +11,7 @@ import { rmSync, writeFileSync, } from "fs"; +import { logBuildPlugin } from "src/util.mjs"; const NODE_VERSION = "14"; const CHROME_VERSION = "91"; @@ -20,43 +21,47 @@ const production = process.argv.includes("--production"); const dirname = path.dirname(fileURLToPath(import.meta.url)); +const distDir = production ? "dist-bundle" : "dist"; + +// Delete old builds to prevent issues/confusion from leftover files +rmSync("dist", { recursive: true, force: true }); +rmSync("dist-bundle", { recursive: true, force: true }); +rmSync("replugged.asar", { force: true }); + const preBundle: esbuild.Plugin = { name: "preBundle", setup: (build) => { build.onEnd(() => { - if (!existsSync("dist/i18n")) { - mkdirSync("dist/i18n"); + if (!existsSync(`${distDir}/i18n`)) { + mkdirSync(`${distDir}/i18n`); } readdirSync("i18n").forEach((file) => { if (file.endsWith(".json")) { - copyFileSync(`i18n/${file}`, `dist/i18n/${file}`); + copyFileSync(`i18n/${file}`, `${distDir}/i18n/${file}`); } }); const mainPackage = JSON.parse(readFileSync("package.json", "utf-8")); writeFileSync( - "dist/package.json", + `${distDir}/package.json`, JSON.stringify({ main: "main.js", name: "replugged", version: mainPackage.version, }), ); - asar.createPackage("dist", "replugged.asar"); + asar.createPackage(`${distDir}`, "replugged.asar"); }); }, }; const plugins: esbuild.Plugin[] = []; +if (!watch) plugins.push(logBuildPlugin); if (production) { - rmSync("dist", { recursive: true, force: true }); plugins.push(preBundle); -} else { - rmSync("dist/i18n", { recursive: true, force: true }); - rmSync("dist/package.json", { force: true }); } const common: esbuild.BuildOptions = { @@ -66,36 +71,46 @@ const common: esbuild.BuildOptions = { sourcemap: !production, format: "cjs" as esbuild.Format, logLevel: "info", - watch, plugins, + metafile: true, }; -Promise.all([ +const contexts = await Promise.all([ // Main - esbuild.build({ + esbuild.context({ ...common, entryPoints: ["src/main/index.ts"], platform: "node", target: `node${NODE_VERSION}`, - outfile: "dist/main.js", + outfile: `${distDir}/main.js`, external: ["electron", "original-fs"], }), // Preload - esbuild.build({ + esbuild.context({ ...common, entryPoints: ["src/preload.ts"], platform: "node", target: [`node${NODE_VERSION}`, `chrome${CHROME_VERSION}`], - outfile: "dist/preload.js", + outfile: `${distDir}/preload.js`, external: ["electron"], }), // Renderer - esbuild.build({ + esbuild.context({ ...common, entryPoints: ["src/renderer/index.ts"], platform: "browser", target: `chrome${CHROME_VERSION}`, - outfile: "dist/renderer.js", + outfile: `${distDir}/renderer.js`, format: "esm", }), ]); +await Promise.all( + contexts.map(async (context) => { + if (watch) { + await context.watch(); + } else { + await context.rebuild().catch(() => {}); + context.dispose(); + } + }), +); diff --git a/scripts/create-import-wrappers.mts b/scripts/create-import-wrappers.mts new file mode 100644 index 000000000..7855cfe60 --- /dev/null +++ b/scripts/create-import-wrappers.mts @@ -0,0 +1,35 @@ +import { writeFileSync } from "node:fs"; +import { basename, join } from "node:path"; + +type LocationsRegistry = + | string[] + | { + [x: string]: LocationsRegistry; + }; + +const locations = { + renderer: { + modules: ["logger", "webpack", "i18n", "injector", "common", "components"], + apis: ["commands", "notices", "settings"], + util: ["."], + }, + types: ["."], +}; + +function createWrappers(currentPath: string[], subpaths: LocationsRegistry): void { + if (Array.isArray(subpaths)) { + for (const subpath of subpaths) { + const fullPath = join(...currentPath, subpath); + const dtsContents = `export * from "./${fullPath}";`; + writeFileSync(`${basename(fullPath)}.d.ts`, dtsContents); + } + } else { + for (const subpath in subpaths) { + currentPath.push(subpath); + createWrappers(currentPath, subpaths[subpath]); + currentPath.pop(); + } + } +} + +createWrappers(["./dist"], locations); diff --git a/src/main/index.ts b/src/main/index.ts index 92d04769f..acbb2dc45 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -53,7 +53,7 @@ class BrowserWindow extends electron.BrowserWindow { // opts.webPreferences.contextIsolation = false; // shrug } else { // Splash Screen on macOS (Host 0.0.262+) & Windows (Host 0.0.293 / 1.0.17+) - // opts.webPreferences.preload = join(__dirname, './preloadSplash.js'); + opts.webPreferences.preload = join(__dirname, "./preload.js"); } } diff --git a/src/main/ipc/settings.ts b/src/main/ipc/settings.ts index d821560c3..f08f4811e 100644 --- a/src/main/ipc/settings.ts +++ b/src/main/ipc/settings.ts @@ -13,7 +13,6 @@ const SETTINGS_DIR = CONFIG_PATHS.settings; export function getSettingsPath(namespace: string): string { const resolved = resolve(SETTINGS_DIR, `${namespace}.json`); - console.log(resolved, SETTINGS_DIR, resolved.startsWith(SETTINGS_DIR)); if (!resolved.startsWith(`${SETTINGS_DIR}${sep}`)) { // Ensure file changes are restricted to the base path throw new Error("Invalid namespace"); diff --git a/src/main/ipc/themes.ts b/src/main/ipc/themes.ts index 84f24b563..740bed3f3 100644 --- a/src/main/ipc/themes.ts +++ b/src/main/ipc/themes.ts @@ -22,7 +22,7 @@ async function getTheme(path: string): Promise { const manifestPath = join(THEMES_DIR, path, "manifest.json"); if (!manifestPath.startsWith(`${THEMES_DIR}${sep}`)) { // Ensure file changes are restricted to the base path - throw new Error("Invalid plugin name"); + throw new Error("Invalid theme name"); } const manifest: unknown = JSON.parse( diff --git a/src/renderer/apis/commands.ts b/src/renderer/apis/commands.ts index ca761bb8a..81e04edaa 100644 --- a/src/renderer/apis/commands.ts +++ b/src/renderer/apis/commands.ts @@ -1,31 +1,250 @@ -import type { RepluggedCommand } from "../../types"; +import type { Channel, Guild, User } from "discord-types/general"; +import type { + AnyRepluggedCommand, + CommandOptionReturn, + CommandOptions, + GetCommandOption, + GetCommandOptions, + GetValueType, + RepluggedCommand, + RepluggedCommandResult, + RepluggedCommandSection, +} from "../../types"; +// eslint-disable-next-line no-duplicate-imports +import { ApplicationCommandOptionType } from "../../types"; +import { constants, i18n, messages, users } from "../modules/common"; +import type { Store } from "../modules/common/flux"; import { Logger } from "../modules/logger"; +import { filters, getByStoreName, waitForModule } from "../modules/webpack"; -const commandsLogger = Logger.api("Commands"); +const logger = Logger.api("Commands"); -class CommandsAPI extends EventTarget { - public commands = new Map(); +let RepluggedUser: User | undefined; - public registerCommand(command: RepluggedCommand): void { - if (this.commands.has(command.name)) { - commandsLogger.error(`Command “${command.name}” is already registered!`); - return; +interface CommandsAndSection { + section: RepluggedCommandSection; + commands: Map; +} + +void waitForModule(filters.bySource(".isStaffPersonal=")).then((User) => { + RepluggedUser = new User({ + avatar: "replugged", + id: "replugged", + bot: true, + username: "Replugged", + system: true, + }); +}); + +export const commandAndSections = new Map(); + +export const defaultSection: RepluggedCommandSection = Object.freeze({ + id: "replugged", + name: "Replugged", + type: 1, + icon: "https://cdn.discordapp.com/attachments/1000955992068079716/1004196106055454820/Replugged-Logo.png", +}); + +export class CommandInteraction { + public options: T[]; + public channel: Channel; + public guild: Guild; + public constructor(props: { options: T[]; channel: Channel; guild: Guild }) { + const UploadAttachmentStore = getByStoreName< + Store & { + getUpload: ( + channelId: string, + optionName: string, + draftType: 0, + ) => { uploadedFilename?: string; item?: { file: File } } | undefined; + } + >("UploadAttachmentStore")!; + this.options = props.options; + this.channel = props.channel; + this.guild = props.guild; + for (const option of this.options.filter( + (o) => o.type === ApplicationCommandOptionType.Attachment, + )) { + const { uploadedFilename, item } = + UploadAttachmentStore.getUpload(props.channel.id, option.name, 0) ?? {}; + option.value = { uploadedFilename, file: item?.file }; } + } - this.commands.set(command.name, command); - this.dispatchEvent(new CustomEvent("rpCommandAdded", { detail: { command } })); + public getValue( + name: K, + defaultValue?: D, + ): GetValueType, D> { + return (this.options.find((o) => o.name === name)?.value ?? defaultValue) as GetValueType< + GetCommandOption, + D + >; } +} + +/** + * @internal + * @hidden + */ +async function executeCommand( + cmdExecutor: + | (( + interaction: CommandInteraction>, + ) => Promise | RepluggedCommandResult) + | undefined, + args: Array>, + currentInfo: { guild: Guild; channel: Channel }, + command: RepluggedCommand, +): Promise { + try { + const currentChannelId = currentInfo.channel.id; + const loadingMessage = messages.createBotMessage({ + channelId: currentChannelId, + content: "", + loggingName: "Replugged", + }); + + Object.assign(loadingMessage, { + flags: constants.MessageFlags.EPHEMERAL + constants.MessageFlags.LOADING, // adding loading too + state: "SENDING", // Keep it a little faded + interaction: { + // eslint-disable-next-line @typescript-eslint/naming-convention + name_localized: command.displayName, + name: command.name, + type: command.type, + id: command.id, + user: users.getCurrentUser(), + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + interaction_data: { + name: command.displayName, + }, + type: 20, + author: RepluggedUser ?? loadingMessage.author, + }); + messages.receiveMessage(currentChannelId, loadingMessage, true); + const interaction = new CommandInteraction({ options: args, ...currentInfo }); + const result = await cmdExecutor?.(interaction); + messages.dismissAutomatedMessage(loadingMessage); - public unregisterCommand(name: string): void { - if (this.commands.has(name)) { - this.dispatchEvent( - new CustomEvent("rpCommandRemoved", { detail: { command: this.commands.get(name) } }), - ); - this.commands.delete(name); + if ((!result?.result && !result?.embeds) || !currentChannelId) return; + + if (result.send) { + void messages.sendMessage(currentChannelId, { + content: result.result!, + invalidEmojis: [], + validNonShortcutEmojis: [], + tts: false, + }); } else { - commandsLogger.error(`Command “${name}” is not registered!`); + const botMessage = messages.createBotMessage({ + channelId: currentChannelId, + content: result.result || "", + embeds: result.embeds || [], + loggingName: "Replugged", + }); + + Object.assign(botMessage, { + interaction: { + // eslint-disable-next-line @typescript-eslint/naming-convention + name_localized: command.displayName, + name: command.name, + type: command.type, + id: command.id, + user: users.getCurrentUser(), + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + interaction_data: { + name: command.displayName, + }, + type: 20, + author: RepluggedUser ?? botMessage.author, + }); + messages.receiveMessage(currentChannelId, botMessage, true); } + } catch (error) { + logger.error(error); + const currentChannelId = currentInfo.channel.id; + const botMessage = messages.createBotMessage({ + channelId: currentChannelId, + content: i18n.Messages.REPLUGGED_COMMAND_ERROR_GENERIC, + embeds: [], + loggingName: "Replugged", + }); + + Object.assign(botMessage, { + interaction: { + // eslint-disable-next-line @typescript-eslint/naming-convention + name_localized: command.displayName, + name: command.name, + type: command.type, + id: command.id, + user: users.getCurrentUser(), + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + interaction_data: { + name: command.displayName, + }, + type: 20, + author: RepluggedUser ?? botMessage.author, + }); + + messages.receiveMessage(currentChannelId, botMessage, true); } } -export default new CommandsAPI(); +export class CommandManager { + #section: RepluggedCommandSection; + #unregister: Array<() => void>; + public constructor() { + this.#section = defaultSection; + this.#section.type ??= 1; + this.#unregister = []; + } + + /** + * Code to register an slash command + * @param cmd Slash Command to be registered + * @returns An Callback to unregister the slash command + */ + public registerCommand(command: RepluggedCommand): () => void { + if (!commandAndSections.has(this.#section.id)) { + commandAndSections.set(this.#section.id, { + section: this.#section, + commands: new Map(), + }); + } + const currentSection = commandAndSections.get(this.#section.id); + command.applicationId = currentSection?.section.id; + command.displayName ??= command.name; + command.displayDescription ??= command.description; + command.type = 2; + command.id ??= command.name; + + command.execute ??= (args, currentInfo) => { + void executeCommand(command.executor, args, currentInfo, command); + }; + + command.options?.map((option) => { + option.serverLocalizedName ??= option.displayName; + option.displayName ??= option.name; + option.displayDescription ??= option.description; + return option; + }); + + currentSection?.commands.set(command.id, command as AnyRepluggedCommand); + + const uninject = (): void => { + void currentSection?.commands.delete(command.id!); + this.#unregister = this.#unregister.filter((u) => u !== uninject); + }; + this.#unregister.push(uninject); + return uninject; + } + /** + * Code to unregister all slash commands registered with this class + */ + public unregisterAllCommands(): void { + for (const unregister of this.#unregister) unregister(); + } +} diff --git a/src/renderer/coremods/commands/commands.ts b/src/renderer/coremods/commands/commands.ts new file mode 100644 index 000000000..7377fe0f0 --- /dev/null +++ b/src/renderer/coremods/commands/commands.ts @@ -0,0 +1,418 @@ +import { Messages } from "@common/i18n"; +import { Injector, plugins, themes } from "@replugged"; +import { ApplicationCommandOptionType } from "../../../types"; + +const injector = new Injector(); + +export function loadCommands(): void { + injector.utils.registerSlashCommand({ + name: Messages.REPLUGGED_COMMAND_ENABLE_NAME, + description: Messages.REPLUGGED_COMMAND_ENABLE_DESC, + options: [ + { + name: "addon", + displayName: Messages.REPLUGGED_COMMAND_ENABLE_OPTION_ADDON_NAME, + description: Messages.REPLUGGED_COMMAND_ADDONS_OPTION_ADDON_DESC, + type: ApplicationCommandOptionType.String, + required: true, + get choices() { + const choices = []; + + const disabledPlugins = Array.from(plugins.plugins.values()).filter((plugin) => + plugins.getDisabled().includes(plugin.manifest.id), + ); + + const disabledThemes = Array.from(themes.themes.values()).filter((theme) => + themes.getDisabled().includes(theme.manifest.id), + ); + + choices.push( + ...disabledPlugins + .map((plugin) => ({ + name: plugin.manifest.name, + displayName: `${Messages.REPLUGGED_PLUGIN}: ${plugin.manifest.name}`, + value: plugin.manifest.id, + })) + .sort((a, b) => a.name.localeCompare(b.name)), + ); + choices.push( + ...disabledThemes + .map((theme) => ({ + name: theme.manifest.name, + displayName: `${Messages.REPLUGGED_THEME}: ${theme.manifest.name}`, + value: theme.manifest.id, + })) + .sort((a, b) => a.name.localeCompare(b.name)), + ); + + return choices; + }, + }, + ], + executor: async (interaction) => { + try { + const addonId = interaction.getValue("addon"); + if (plugins.plugins.has(addonId)) { + await plugins.enable(addonId); + } else { + themes.enable(addonId); + } + return { + send: false, + embeds: [ + { + color: 0x1bbb1b, + title: Messages.REPLUGGED_COMMAND_SUCCESS_GENERIC, + description: Messages.REPLUGGED_COMMAND_ENABLE_MESSAGE_ENABLED.format({ + type: plugins.plugins.get(addonId) + ? Messages.REPLUGGED_PLUGIN + : Messages.REPLUGGED_THEME, + name: + plugins.plugins.get(addonId)?.manifest.name ?? + themes.themes.get(addonId)?.manifest.name, + }), + }, + ], + }; + } catch (err) { + return { + send: false, + embeds: [ + { + color: 0xdd2d2d, + title: Messages.REPLUGGED_COMMAND_ERROR_GENERIC, + description: err as string, + }, + ], + }; + } + }, + }); + injector.utils.registerSlashCommand({ + name: Messages.REPLUGGED_COMMAND_DISABLE_NAME, + description: Messages.REPLUGGED_COMMAND_DISABLE_DESC, + options: [ + { + name: "addon", + displayName: Messages.REPLUGGED_COMMAND_DISABLE_OPTION_ADDON_NAME, + description: Messages.REPLUGGED_COMMAND_DISABLE_OPTION_ADDON_DESC, + type: ApplicationCommandOptionType.String, + required: true, + get choices() { + const choices = []; + + const enabledPlugins = Array.from(plugins.plugins.values()).filter( + (plugin) => !plugins.getDisabled().includes(plugin.manifest.id), + ); + + const enabledThemes = Array.from(themes.themes.values()).filter( + (theme) => !themes.getDisabled().includes(theme.manifest.id), + ); + + choices.push( + ...enabledPlugins + .map((plugin) => ({ + name: plugin.manifest.name, + displayName: `${Messages.REPLUGGED_PLUGIN}: ${plugin.manifest.name}`, + value: plugin.manifest.id, + })) + .sort((a, b) => a.name.localeCompare(b.name)), + ); + choices.push( + ...enabledThemes + .map((theme) => ({ + name: theme.manifest.name, + displayName: `${Messages.REPLUGGED_THEME}: ${theme.manifest.name}`, + value: theme.manifest.id, + })) + .sort((a, b) => a.name.localeCompare(b.name)), + ); + + return choices; + }, + }, + ], + executor: async (interaction) => { + try { + const addonId = interaction.getValue("addon"); + if (plugins.plugins.has(addonId)) { + await plugins.disable(addonId); + } else { + themes.disable(addonId); + } + return { + send: false, + embeds: [ + { + color: 0x1bbb1b, + title: Messages.REPLUGGED_COMMAND_SUCCESS_GENERIC, + description: Messages.REPLUGGED_COMMAND_DISABLE_MESSAGE_ENABLED.format({ + type: plugins.plugins.get(addonId) + ? Messages.REPLUGGED_PLUGIN + : Messages.REPLUGGED_THEME, + name: + plugins.plugins.get(addonId)?.manifest.name ?? + themes.themes.get(addonId)?.manifest.name, + }), + }, + ], + }; + } catch (err) { + return { + send: false, + embeds: [ + { + color: 0xdd2d2d, + title: Messages.REPLUGGED_COMMAND_ERROR_GENERIC, + description: err as string, + }, + ], + }; + } + }, + }); + injector.utils.registerSlashCommand({ + name: Messages.REPLUGGED_COMMAND_RELOAD_NAME, + description: Messages.REPLUGGED_COMMAND_RELOAD_DESC, + options: [ + { + name: "addon", + displayName: Messages.REPLUGGED_COMMAND_RELOAD_OPTION_ADDON_NAME, + description: Messages.REPLUGGED_COMMAND_RELOAD_OPTION_ADDON_DESC, + type: ApplicationCommandOptionType.String, + required: true, + get choices() { + const choices = []; + + const enabledPlugins = Array.from(plugins.plugins.values()); + const enabledThemes = Array.from(themes.themes.values()); + + choices.push( + ...enabledPlugins + .map((plugin) => ({ + name: plugin.manifest.name, + displayName: `${Messages.REPLUGGED_PLUGIN}: ${plugin.manifest.name}`, + value: plugin.manifest.id, + })) + .sort((a, b) => a.name.localeCompare(b.name)), + ); + choices.push( + ...enabledThemes + .map((theme) => ({ + name: theme.manifest.name, + displayName: `${Messages.REPLUGGED_THEME}: ${theme.manifest.name}`, + value: theme.manifest.id, + })) + .sort((a, b) => a.name.localeCompare(b.name)), + ); + + return choices; + }, + }, + ], + executor: async (interaction) => { + try { + const addonId = interaction.getValue("addon"); + if (plugins.plugins.has(addonId)) { + await plugins.reload(addonId); + } else { + themes.reload(addonId); + } + return { + send: false, + embeds: [ + { + color: 0x1bbb1b, + title: Messages.REPLUGGED_COMMAND_SUCCESS_GENERIC, + description: Messages.REPLUGGED_COMMAND_RELOAD_MESSAGE_ENABLED.format({ + type: plugins.plugins.get(addonId) + ? Messages.REPLUGGED_PLUGIN + : Messages.REPLUGGED_THEME, + name: + plugins.plugins.get(addonId)?.manifest.name ?? + themes.themes.get(addonId)?.manifest.name, + }), + }, + ], + }; + } catch (err) { + return { + send: false, + embeds: [ + { + color: 0xdd2d2d, + title: Messages.REPLUGGED_COMMAND_ERROR_GENERIC, + description: err as string, + }, + ], + }; + } + }, + }); + injector.utils.registerSlashCommand({ + name: Messages.REPLUGGED_COMMAND_LIST_NAME, + description: Messages.REPLUGGED_COMMAND_LIST_DESC, + options: [ + { + name: "send", + displayName: Messages.REPLUGGED_COMMAND_LIST_OPTION_SEND_NAME, + description: Messages.REPLUGGED_COMMAND_LIST_OPTION_SEND_DESC, + type: ApplicationCommandOptionType.Boolean, + required: false, + }, + { + name: "type", + displayName: Messages.REPLUGGED_COMMAND_LIST_OPTION_TYPE_NAME, + description: Messages.REPLUGGED_COMMAND_LIST_OPTION_TYPE_DESC, + type: ApplicationCommandOptionType.String, + required: true, + choices: [ + { + name: Messages.REPLUGGED_COMMAND_LIST_OPTION_TYPE_CHOICE_THEME, + displayName: Messages.REPLUGGED_COMMAND_LIST_OPTION_TYPE_CHOICE_THEME, + value: "theme", + }, + { + name: Messages.REPLUGGED_COMMAND_LIST_OPTION_TYPE_CHOICE_PLUGIN, + displayName: Messages.REPLUGGED_COMMAND_LIST_OPTION_TYPE_CHOICE_PLUGIN, + value: "plugin", + }, + ], + }, + { + name: "version", + displayName: Messages.REPLUGGED_COMMAND_LIST_OPTION_VERSION_NAME, + description: Messages.REPLUGGED_COMMAND_LIST_OPTION_VERSION_DESC, + type: ApplicationCommandOptionType.Boolean, + required: false, + }, + { + name: "status", + displayName: Messages.REPLUGGED_COMMAND_LIST_OPTION_STATUS_NAME, + description: Messages.REPLUGGED_COMMAND_LIST_OPTION_STATUS_DESC, + type: ApplicationCommandOptionType.String, + required: false, + choices: [ + { + name: Messages.REPLUGGED_COMMAND_LIST_OPTION_STATUS_CHOICE_ENABLED, + displayName: Messages.REPLUGGED_COMMAND_LIST_OPTION_STATUS_CHOICE_ENABLED, + value: "enabled", + }, + { + name: Messages.REPLUGGED_COMMAND_LIST_OPTION_STATUS_CHOICE_DISABLED, + displayName: Messages.REPLUGGED_COMMAND_LIST_OPTION_STATUS_CHOICE_DISABLED, + value: "disabled", + }, + { + name: Messages.REPLUGGED_COMMAND_LIST_OPTION_STATUS_CHOICE_BOTH, + displayName: Messages.REPLUGGED_COMMAND_LIST_OPTION_STATUS_CHOICE_BOTH, + value: "default", + }, + ], + }, + ], + executor: (interaction) => { + try { + const send = interaction.getValue("send", false); + const addonType = interaction.getValue("type"); + const version = interaction.getValue("version", true); + const listType = interaction.getValue("status", "default"); + + const generateListString = ( + items: Array<{ name: string; version: string }>, + typeName: string, + ): string => + `## ${typeName} (${items.length})${items.length ? ":\n•" : ""} ${items + .map((item) => (version ? `${item.name} (v${item.version})` : item.name)) + .join("\n• ")}`; + + switch (addonType) { + case "plugin": { + const allPlugins = Array.from(plugins.plugins.values()) + .map((p) => p.manifest) + .sort((a, b) => a.name.localeCompare(b.name)); + const enablePlugins = allPlugins.filter((p) => !plugins.getDisabled().includes(p.id)); + const disabledPlugins = allPlugins.filter((p) => plugins.getDisabled().includes(p.id)); + + const enabledString = generateListString( + enablePlugins, + Messages.REPLUGGED_COMMAND_LIST_HEADER_ENABLED.format({ + type: Messages.REPLUGGED_PLUGINS, + }), + ); + const disabledString = generateListString( + disabledPlugins, + Messages.REPLUGGED_COMMAND_LIST_HEADER_DISABLED.format({ + type: Messages.REPLUGGED_PLUGINS, + }), + ); + + const result = + listType === "enabled" + ? enabledString + : listType === "disabled" + ? disabledString + : `${enabledString}\n\n${disabledString}`; + + return { + send, + result, + }; + } + case "theme": { + const allThemes = Array.from(themes.themes.values()) + .map((t) => t.manifest) + .sort((a, b) => a.name.localeCompare(b.name)); + const enableThemes = allThemes.filter((t) => !plugins.getDisabled().includes(t.id)); + const disabledThemes = allThemes.filter((t) => plugins.getDisabled().includes(t.id)); + + const enabledString = generateListString( + enableThemes, + Messages.REPLUGGED_COMMAND_LIST_HEADER_ENABLED.format({ + type: Messages.REPLUGGED_THEMES, + }), + ); + const disabledString = generateListString( + disabledThemes, + Messages.REPLUGGED_COMMAND_LIST_HEADER_DISABLED.format({ + type: Messages.REPLUGGED_THEMES, + }), + ); + + const result = + listType === "enabled" + ? enabledString + : listType === "disabled" + ? disabledString + : `${enabledString}\n\n${disabledString}`; + + return { + send, + result, + }; + } + default: + return { + send: false, + result: Messages.REPLUGGED_COMMAND_LIST_ERROR_SPECIFY, + }; + } + } catch (err) { + return { + send: false, + embeds: [ + { + color: 0xdd2d2d, + title: Messages.REPLUGGED_COMMAND_ERROR_GENERIC, + description: err as string, + }, + ], + }; + } + }, + }); +} + +export function unloadCommands(): void { + injector.uninjectAll(); +} diff --git a/src/renderer/coremods/commands/index.ts b/src/renderer/coremods/commands/index.ts new file mode 100644 index 000000000..8d0fa9dc4 --- /dev/null +++ b/src/renderer/coremods/commands/index.ts @@ -0,0 +1,295 @@ +import type { AnyRepluggedCommand, RepluggedCommandSection } from "../../../types"; +import { Injector } from "../../modules/injector"; +import { Logger } from "../../modules/logger"; +import { + filters, + getExportsForProps, + getFunctionKeyBySource, + waitForModule, + waitForProps, +} from "../../modules/webpack"; + +import { commandAndSections, defaultSection } from "../../apis/commands"; +import { loadCommands, unloadCommands } from "./commands"; + +const logger = Logger.api("Commands"); +const injector = new Injector(); + +interface ApplicationCommandSearchStoreMod { + [key: string]: (...args: unknown[]) => + | { + sectionDescriptors: RepluggedCommandSection[]; + commands: AnyRepluggedCommand[]; + filteredSectionId: string | null; + activeSections: RepluggedCommandSection[]; + commandsByActiveSection: Array<{ + section: RepluggedCommandSection; + data: AnyRepluggedCommand[]; + }>; + } + | undefined; +} + +interface ApplicationCommandSearchStore { + getChannelState: (...args: unknown[]) => + | { + applicationSections: RepluggedCommandSection[]; + applicationCommands: AnyRepluggedCommand[]; + } + | undefined; + getApplicationSections: (...args: unknown[]) => RepluggedCommandSection[] | undefined; + useSearchManager: (...args: unknown[]) => unknown; + getQueryCommands: (...args: [string, string, string]) => AnyRepluggedCommand[] | undefined; +} + +async function injectRepluggedBotIcon(): Promise { + // Adds Avatar for replugged to default avatar to be used by system bot just like clyde + // Ain't removing it on stop because we have checks here + const DefaultAvatars = await waitForProps<{ + BOT_AVATARS: Record | undefined; + }>("BOT_AVATARS"); + if (DefaultAvatars.BOT_AVATARS) { + DefaultAvatars.BOT_AVATARS.replugged = defaultSection.icon; + } else { + logger.error("Error while injecting custom icon for slash command replies."); + } +} + +async function injectRepluggedSectionIcon(): Promise { + // Patches the function which gets icon URL for slash command sections + // makes it return the custom url if it's our section + const AssetsUtils = await waitForProps<{ + getApplicationIconURL: (args: { id: string; icon: string }) => string; + }>("getApplicationIconURL"); + injector.after(AssetsUtils, "getApplicationIconURL", ([section], res) => + commandAndSections.has(section.id) ? commandAndSections.get(section.id)?.section.icon : res, + ); +} + +async function injectApplicationCommandSearchStore(): Promise { + // The module which contains the store + const ApplicationCommandSearchStoreMod = await waitForModule( + filters.bySource("ApplicationCommandSearchStore"), + ); + const storeModFnKey = getFunctionKeyBySource( + ApplicationCommandSearchStoreMod, + "APPLICATION_COMMAND_SEARCH_STORE_UPDATE", + ); + + // Base handler function for ApplicationCommandSearchStore which is ran to get the info in store + // commands are mainly added here + injector.after(ApplicationCommandSearchStoreMod, storeModFnKey!, (_, res) => { + const commandAndSectionsArray = Array.from(commandAndSections.values()).filter( + (commandAndSection) => commandAndSection.commands.size, + ); + if (!res || !commandAndSectionsArray.length) return res; + if ( + !Array.isArray(res.sectionDescriptors) || + !commandAndSectionsArray.every((commandAndSection) => + res.sectionDescriptors.some((section) => section.id === commandAndSection.section.id), + ) + ) { + const sectionsToAdd = commandAndSectionsArray + .map((commandAndSection) => commandAndSection.section) + .filter((section) => !res.sectionDescriptors.includes(section)); + if (res.sectionDescriptors.some((section) => section.id === "-2")) { + res.sectionDescriptors.splice(1, 0, ...sectionsToAdd); + } else { + res.sectionDescriptors = Array.isArray(res.sectionDescriptors) + ? [...sectionsToAdd, ...res.sectionDescriptors] + : sectionsToAdd; + } + } + if ( + res.filteredSectionId === null || + commandAndSectionsArray.some( + (commandAndSection) => res.filteredSectionId === commandAndSection.section.id, + ) + ) { + const sectionsToAdd = commandAndSectionsArray + .map((commandAndSection) => commandAndSection.section) + .filter( + (section) => + (res.filteredSectionId == null || res.filteredSectionId === section.id) && + !res.activeSections.includes(section), + ); + if (res.activeSections.some((section) => section.id === "-2")) { + res.activeSections.splice(1, 0, ...sectionsToAdd); + } else { + res.activeSections = Array.isArray(res.activeSections) + ? [...sectionsToAdd, ...res.activeSections] + : sectionsToAdd; + } + + const commandsBySectionToAdd = commandAndSectionsArray + .filter( + (commandAndSection) => + (res.filteredSectionId !== null + ? res.filteredSectionId === commandAndSection.section.id + : true) && + !res.commandsByActiveSection.some( + (activeCommandAndSection) => + activeCommandAndSection.section.id === commandAndSection.section.id, + ), + ) + .map((commandAndSection) => ({ + section: commandAndSection.section, + data: Array.from(commandAndSection.commands.values()), + })); + + if ( + res.commandsByActiveSection.some( + (activeCommandAndSections) => activeCommandAndSections.section.id === "-2", + ) + ) { + res.commandsByActiveSection.splice(1, 0, ...commandsBySectionToAdd); + } else { + res.commandsByActiveSection = Array.isArray(res.commandsByActiveSection) + ? [...commandsBySectionToAdd, ...res.commandsByActiveSection] + : commandsBySectionToAdd; + } + } + if ( + !Array.isArray(res.commands) || + commandAndSectionsArray.some((commandAndSection) => + Array.from(commandAndSection.commands.values()).some( + (command) => !res.commands.includes(command), + ), + ) + ) { + const commandsToAdd = commandAndSectionsArray + .map((commandAndSection) => Array.from(commandAndSection.commands.values())) + .flat(10); + res.commands = Array.isArray(res.commands) + ? [...res.commands.filter((command) => !commandsToAdd.includes(command)), ...commandsToAdd] + : commandsToAdd; + } + return res; + }); + + // The store itself + const ApplicationCommandSearchStore = getExportsForProps( + ApplicationCommandSearchStoreMod, + ["getApplicationSections", "getChannelState", "getQueryCommands"], + )!; + + // Channel state gets update with each character entered in text box and search so we patch this to keep our custom section + // even after updates happen + injector.after(ApplicationCommandSearchStore, "getChannelState", (_, res) => { + const commandAndSectionsArray = Array.from(commandAndSections.values()).filter( + (commandAndSection) => commandAndSection.commands.size, + ); + if (!res || !commandAndSectionsArray.length) return res; + if ( + !Array.isArray(res.applicationSections) || + !commandAndSectionsArray.every((commandAndSection) => + res.applicationSections.some((section) => section.id === commandAndSection.section.id), + ) + ) { + const sectionsToAdd = commandAndSectionsArray.map( + (commandAndSection) => commandAndSection.section, + ); + res.applicationSections = Array.isArray(res.applicationSections) + ? [...sectionsToAdd, ...res.applicationSections] + : sectionsToAdd; + } + if ( + !Array.isArray(res.applicationCommands) || + commandAndSectionsArray.some((commandAndSection) => + Array.from(commandAndSection.commands.values()).some( + (command) => !res.applicationCommands.includes(command), + ), + ) + ) { + const commandsToAdd = commandAndSectionsArray + .map((commandAndSection) => Array.from(commandAndSection.commands.values())) + .flat(10); + res.applicationCommands = Array.isArray(res.applicationCommands) + ? [ + ...commandsToAdd, + ...res.applicationCommands.filter((command) => !commandsToAdd.includes(command)), + ] + : commandsToAdd; + } + return res; + }); + + // Makes sure if our custom section is included or not + // Add it if not + injector.after(ApplicationCommandSearchStore, "getApplicationSections", (_, res) => { + res ??= []; + const commandAndSectionsArray = Array.from(commandAndSections.values()).filter( + (commandAndSection) => commandAndSection.commands.size, + ); + if (!commandAndSectionsArray.length) return; + if ( + !commandAndSectionsArray.every( + (commandAndSection) => res?.some((section) => section.id === commandAndSection.section.id), + ) + ) { + const sectionsToAdd = commandAndSectionsArray + .map((commandAndSection) => commandAndSection.section) + .filter((section) => res?.some((existingSections) => section.id === existingSections.id)); + res.push(...sectionsToAdd); + } + return res; + }); + + // Slash command search patched to return our slash commands too + // only those which match tho + injector.after(ApplicationCommandSearchStore, "getQueryCommands", ([_, __, query], res) => { + if (!query || query.startsWith("/")) return res; + + res ??= []; + const commandsToAdd = Array.from(commandAndSections.values()) + .filter((commandAndSection) => commandAndSection.commands.size) + .map((commandAndSection) => Array.from(commandAndSection.commands.values())) + .flat(10); + for (const command of commandsToAdd) { + const exists = res.some((c) => c.id === command.id); + + if (exists || !command.name.includes(query)) { + continue; + } + + try { + res.unshift(command); + } catch { + res = [command, ...res]; + } + } + + return res; + }); +} + +async function injectProfileFetch(): Promise { + const mod = await waitForModule< + Record< + string, + ( + id: string, + avatar: string, + { guildId, channelId }: { guildId: string; channelId: string }, + ) => Promise + > + >(filters.bySource(".preloadUserBanner,"), { raw: true }); + const fnKey = getFunctionKeyBySource(mod.exports, ".apply(this"); + injector.instead(mod.exports, fnKey!, (args, res) => { + if (args[1] === defaultSection.icon) { + return; + } + return res(...args); + }); +} +export async function start(): Promise { + await injectRepluggedBotIcon(); + await injectRepluggedSectionIcon(); + await injectApplicationCommandSearchStore(); + await injectProfileFetch(); + loadCommands(); +} +export function stop(): void { + injector.uninjectAll(); + unloadCommands(); +} diff --git a/src/renderer/coremods/commands/plaintextPatches.ts b/src/renderer/coremods/commands/plaintextPatches.ts new file mode 100644 index 000000000..0b80980c4 --- /dev/null +++ b/src/renderer/coremods/commands/plaintextPatches.ts @@ -0,0 +1,14 @@ +import type { PlaintextPatch } from "src/types"; + +export default [ + { + //disables api request to find commands if its added by replugged + find: "filteredSectionId:null", + replacements: [ + { + match: /\w+\({applicationId:(\w+)}/, + replace: (suffix, id) => `${id} == "replugged"||${suffix}`, + }, + ], + }, +] as PlaintextPatch[]; diff --git a/src/renderer/coremods/installer/AddonEmbed.tsx b/src/renderer/coremods/installer/AddonEmbed.tsx index f9fd91b71..0ff620242 100644 --- a/src/renderer/coremods/installer/AddonEmbed.tsx +++ b/src/renderer/coremods/installer/AddonEmbed.tsx @@ -5,9 +5,8 @@ import { Logger } from "@replugged"; import { getByProps } from "@webpack"; import { openExternal } from "src/renderer/util"; import { CheckResultSuccess } from "src/types"; -import { InstallLinkProps } from "."; import { getSourceLink } from "../settings/pages"; -import { authorList, checkIsInstalled, getInfo, install } from "./util"; +import { InstallLinkProps, authorList, checkIsInstalled, getInfo, install } from "./util"; import "./addonEmbed.css"; diff --git a/src/renderer/coremods/installer/commands.ts b/src/renderer/coremods/installer/commands.ts new file mode 100644 index 000000000..26b15f9dd --- /dev/null +++ b/src/renderer/coremods/installer/commands.ts @@ -0,0 +1,83 @@ +import { Injector } from "@replugged"; +import { ApplicationCommandOptionType } from "src/types"; +import { INSTALLER_SOURCES, InstallerSource, installFlow, parseInstallLink } from "./util"; +import { Messages } from "@common/i18n"; + +/** + * A map of display names for installer sources. + */ +const sourceDisplayNames: Record = { + github: "GitHub", + store: Messages.REPLUGGED_STORE, +}; + +export function loadCommands(injector: Injector): void { + injector.utils.registerSlashCommand({ + name: "install", + displayName: Messages.REPLUGGED_COMMAND_INSTALL_NAME, + description: Messages.REPLUGGED_COMMAND_INSTALL_DESC, + options: [ + { + name: "addon", + displayName: Messages.REPLUGGED_COMMAND_INSTALL_OPTION_ADDON_NAME, + description: Messages.REPLUGGED_COMMAND_INSTALL_OPTION_ADDON_DESC, + type: ApplicationCommandOptionType.String, + required: true, + }, + { + name: "source", + displayName: Messages.REPLUGGED_COMMAND_INSTALL_OPTION_SOURCE_NAME, + description: Messages.REPLUGGED_COMMAND_INSTALL_OPTION_SOURCE_DESC, + type: ApplicationCommandOptionType.String, + required: false, + choices: INSTALLER_SOURCES.map((v) => ({ + name: v, + displayName: sourceDisplayNames[v], + value: v, + })), + }, + { + name: "id", + displayName: Messages.REPLUGGED_COMMAND_INSTALL_OPTION_ID_NAME, + description: Messages.REPLUGGED_COMMAND_INSTALL_OPTION_ID_DESC, + type: ApplicationCommandOptionType.String, + required: false, + }, + ], + + async executor(i) { + let addon = i.getValue("addon"); + let source = i.getValue("source"); + let id = i.getValue("id"); + + const linkParsed = parseInstallLink(addon); + + if (linkParsed) { + ({ identifier: addon, source, id } = linkParsed); + } + + const resp = await installFlow(addon, source, id, false); + + switch (resp.kind) { + case "FAILED": + return { + result: Messages.REPLUGGED_TOAST_INSTALLER_ADDON_FETCH_INFO_FAILED, + }; + case "ALREADY_INSTALLED": + return { + result: Messages.REPLUGGED_ERROR_ALREADY_INSTALLED.format({ name: resp.manifest.name }), + }; + case "CANCELLED": + return { + result: Messages.REPLUGGED_TOAST_INSTALLER_ADDON_CANCELED_INSTALL, + }; + case "SUCCESS": + return { + result: Messages.REPLUGGED_TOAST_INSTALLER_ADDON_INSTALL_SUCCESS.format({ + name: resp.manifest.name, + }), + }; + } + }, + }); +} diff --git a/src/renderer/coremods/installer/index.tsx b/src/renderer/coremods/installer/index.tsx index f796b44bc..a42931b14 100644 --- a/src/renderer/coremods/installer/index.tsx +++ b/src/renderer/coremods/installer/index.tsx @@ -2,13 +2,20 @@ import { Injector, Logger } from "@replugged"; import { filters, getFunctionKeyBySource, waitForModule } from "src/renderer/modules/webpack"; import { ObjectExports } from "src/types"; import { registerRPCCommand } from "../rpc"; -import { InstallResponse, InstallerSource, installFlow, isValidSource } from "./util"; +import { + InstallLinkProps, + InstallResponse, + InstallerSource, + installFlow, + parseInstallLink, +} from "./util"; import { plugins } from "src/renderer/managers/plugins"; import { themes } from "src/renderer/managers/themes"; import AddonEmbed from "./AddonEmbed"; import { generalSettings } from "../settings/pages"; import type { Capture, DefaultInRule } from "simple-markdown"; import { parser } from "@common"; +import { loadCommands } from "./commands"; const injector = new Injector(); const logger = Logger.coremod("Installer"); @@ -18,51 +25,6 @@ interface AnchorProps extends React.ComponentPropsWithoutRef<"a"> { focusProps?: Record; } -export interface InstallLinkProps { - /** Identifier for the addon in the source */ - identifier: string; - /** Updater source type */ - source?: InstallerSource; - /** ID for the addon in that source. Useful for GitHub repositories that have multiple addons. */ - id?: string; -} - -function parseInstallLink(href: string): InstallLinkProps | null { - try { - const url = new URL(href); - const repluggedHostname = new URL(generalSettings.get("apiUrl")).hostname; - if (url.hostname !== repluggedHostname) return null; - - if (url.pathname === "/install") { - const params = url.searchParams; - const identifier = params.get("identifier"); - const source = params.get("source") ?? "store"; - const id = params.get("id") ?? undefined; - if (!identifier) return null; - if (!isValidSource(source)) return null; - return { - identifier, - source, - id, - }; - } - - const storeMatch = url.pathname.match(/^\/store\/([^/]+)$/); - if (storeMatch) { - const identifier = storeMatch[1]; - if (["plugins", "themes"].includes(identifier.toLowerCase())) return null; - return { - identifier, - source: "store", - }; - } - - return null; - } catch { - return null; - } -} - let uninjectFns: Array<() => void> = []; const modalFlows = new Map>(); @@ -169,7 +131,7 @@ async function injectLinks(): Promise { parse: (capture: Capture) => { const installLink = parseInstallLink(capture[1]); return { - ...installLink!, + ...installLink, url: capture[1], }; }, @@ -202,6 +164,7 @@ async function injectLinks(): Promise { export async function start(): Promise { await injectLinks(); injectRpc(); + loadCommands(injector); } export function stop(): void { diff --git a/src/renderer/coremods/installer/util.tsx b/src/renderer/coremods/installer/util.tsx index 516de6b7b..a0c5c0c42 100644 --- a/src/renderer/coremods/installer/util.tsx +++ b/src/renderer/coremods/installer/util.tsx @@ -7,12 +7,12 @@ import { openExternal } from "src/renderer/util"; import type { AnyAddonManifest, CheckResultSuccess } from "src/types"; import * as pluginManager from "../../managers/plugins"; import * as themeManager from "../../managers/themes"; -import { getAddonType, getSourceLink, label } from "../settings/pages"; +import { generalSettings, getAddonType, getSourceLink, label } from "../settings/pages"; const logger = Logger.coremod("Installer"); // First item is the default -const INSTALLER_SOURCES = ["store", "github"] as const; +export const INSTALLER_SOURCES = ["store", "github"] as const; export type InstallerSource = (typeof INSTALLER_SOURCES)[number]; const DEFAULT_INSTALLER_SOURCE: InstallerSource = "store"; @@ -25,6 +25,51 @@ export function isValidSource(type: string): type is InstallerSource { return INSTALLER_SOURCES.includes(type); } +export interface InstallLinkProps { + /** Identifier for the addon in the source */ + identifier: string; + /** Updater source type */ + source?: InstallerSource; + /** ID for the addon in that source. Useful for GitHub repositories that have multiple addons. */ + id?: string; +} + +export function parseInstallLink(href: string): InstallLinkProps | null { + try { + const url = new URL(href); + const repluggedHostname = new URL(generalSettings.get("apiUrl")).hostname; + if (url.hostname !== repluggedHostname) return null; + + if (url.pathname === "/install") { + const params = url.searchParams; + const identifier = params.get("identifier"); + const source = params.get("source") ?? "store"; + const id = params.get("id") ?? undefined; + if (!identifier) return null; + if (!isValidSource(source)) return null; + return { + identifier, + source, + id, + }; + } + + const storeMatch = url.pathname.match(/^\/store\/([^/]+)$/); + if (storeMatch) { + const identifier = storeMatch[1]; + if (["plugins", "themes"].includes(identifier.toLowerCase())) return null; + return { + identifier, + source: "store", + }; + } + + return null; + } catch { + return null; + } +} + export async function getInfo( identifier: string, source?: InstallerSource, diff --git a/src/renderer/index.ts b/src/renderer/index.ts index 462457742..ccce42626 100644 --- a/src/renderer/index.ts +++ b/src/renderer/index.ts @@ -2,5 +2,14 @@ import * as replugged from "./replugged"; window.replugged = replugged; -await replugged.plugins.loadAll(); -await replugged.ignition.ignite(); +type DiscordSplashWindow = Window & { + DiscordSplash?: object; +}; + +// Splash screen +if ((window as DiscordSplashWindow).DiscordSplash) { + await replugged.ignition.startSplash(); +} else { + await replugged.plugins.loadAll(); + await replugged.ignition.ignite(); +} diff --git a/src/renderer/managers/coremods.ts b/src/renderer/managers/coremods.ts index a0ec8744c..ec746b064 100644 --- a/src/renderer/managers/coremods.ts +++ b/src/renderer/managers/coremods.ts @@ -9,6 +9,7 @@ import { default as messagePopover } from "../coremods/messagePopover/plaintextP import { default as notices } from "../coremods/notices/plaintextPatches"; import { default as contextMenu } from "../coremods/contextMenu/plaintextPatches"; import { default as languagePlaintext } from "../coremods/language/plaintextPatches"; +import { default as commandsPlaintext } from "../coremods/commands/plaintextPatches"; import { Logger } from "../modules/logger"; const logger = Logger.api("Coremods"); @@ -31,6 +32,7 @@ export namespace coremods { export let language: Coremod; export let rpc: Coremod; export let watcher: Coremod; + export let commands: Coremod; export let welcome: Coremod; } @@ -55,6 +57,7 @@ export async function startAll(): Promise { coremods.language = await import("../coremods/language"); coremods.rpc = await import("../coremods/rpc"); coremods.watcher = await import("../coremods/watcher"); + coremods.commands = await import("../coremods/commands"); coremods.welcome = await import("../coremods/welcome"); await Promise.all( Object.entries(coremods).map(async ([name, mod]) => { @@ -81,5 +84,6 @@ export function runPlaintextPatches(): void { notices, contextMenu, languagePlaintext, + commandsPlaintext, ].forEach(patchPlaintext); } diff --git a/src/renderer/managers/ignition.ts b/src/renderer/managers/ignition.ts index 74f035b3f..44cfbb752 100644 --- a/src/renderer/managers/ignition.ts +++ b/src/renderer/managers/ignition.ts @@ -91,3 +91,17 @@ export async function ignite(): Promise { await componentsReady(); await start(); } + +export async function startSplash(): Promise { + log("Ignition", "Start", void 0, "Igniting Replugged Splash Screen..."); + const startTime = performance.now(); + + await themes.loadMissing().then(themes.loadAllSplash); + + log( + "Ignition", + "Start", + void 0, + `Finished igniting Replugged Splash Screen in ${performance.now() - startTime}ms`, + ); +} diff --git a/src/renderer/managers/themes.ts b/src/renderer/managers/themes.ts index fe6b09a25..eea566689 100644 --- a/src/renderer/managers/themes.ts +++ b/src/renderer/managers/themes.ts @@ -2,6 +2,7 @@ import { loadStyleSheet } from "../util"; import type { RepluggedTheme } from "../../types"; import type { AddonSettings } from "src/types/addon"; import { init } from "../apis/settings"; +import * as logger from "../modules/logger"; const themeElements = new Map(); @@ -37,31 +38,67 @@ export function unload(id: string): void { } /** - * Load a theme, adding its stylesheet to the DOM + * Load a theme's main variant, adding its stylesheet to the DOM * @param id Theme ID (RDNN) */ export function load(id: string): void { if (!themes.has(id)) { throw new Error(`Theme not found: ${id}`); } - unload(id); const theme = themes.get(id)!; + if (!theme.manifest.main) { + logger.error("Manager", `Theme ${id} does not have a main variant.`); + return; + } + unload(id); + const el = loadStyleSheet(`replugged://theme/${theme.path}/${theme.manifest.main}`); themeElements.set(id, el); } /** - * Load all themes, adding their stylesheets to the DOM. Disabled themes are not loaded. + * Load a theme's splash variant, adding its stylesheet to the DOM + * @param id Theme ID (RDNN) + */ +export function loadSplash(id: string): void { + if (!themes.has(id)) { + throw new Error(`Theme not found: ${id}`); + } + + const theme = themes.get(id)!; + if (!theme.manifest.splash) { + logger.error("Manager", `Theme ${id} does not have a splash variant.`); + return; + } + unload(id); + + const el = loadStyleSheet(`replugged://theme/${theme.path}/${theme.manifest.splash}`); + themeElements.set(id, el); +} + +/** + * Load all themes' main variants, adding their stylesheets to the DOM. Disabled themes are not loaded. */ export function loadAll(): void { for (const id of themes.keys()) { - if (!disabled.includes(id)) { + if (!disabled.includes(id) && themes.get(id)?.manifest.main) { load(id); } } } +/** + * Load all themes' splash variants, adding their stylesheets to the DOM. Disabled themes are not loaded. + */ +export function loadAllSplash(): void { + for (const id of themes.keys()) { + if (!disabled.includes(id) && themes.get(id)?.manifest.splash) { + loadSplash(id); + } + } +} + /** * Unload all themes, removing their stylesheets from the DOM */ @@ -92,13 +129,21 @@ export async function list(): Promise { } /** - * Reload a theme to apply changes + * Reload a theme's main variant to apply changes */ export function reload(id: string): void { unload(id); load(id); } +/** + * Reload a theme's splash variant to apply changes + */ +export function reloadSplash(id: string): void { + unload(id); + loadSplash(id); +} + export function enable(id: string): void { if (!themes.has(id)) { throw new Error(`Theme "${id}" does not exist.`); diff --git a/src/renderer/modules/common/channels.ts b/src/renderer/modules/common/channels.ts index 8bffd8e94..7adc5841e 100644 --- a/src/renderer/modules/common/channels.ts +++ b/src/renderer/modules/common/channels.ts @@ -21,6 +21,7 @@ export interface ChannelStore { getAllThreadsForParent(channelId: string): Channel[]; getBasicChannel(channelId: string): Channel | undefined; getChannel(channelId: string): Channel | undefined; + getChannelIds(guildId?: string): string[]; getDMFromUserId(userId: string): string | undefined; getDMUserIds(): string[]; getGuildChannelsVersion(guildId: string): number; diff --git a/src/renderer/modules/common/constants.ts b/src/renderer/modules/common/constants.ts index cbb28e3aa..5f71311cd 100644 --- a/src/renderer/modules/common/constants.ts +++ b/src/renderer/modules/common/constants.ts @@ -1,6 +1,8 @@ import { virtualMerge } from "src/renderer/util"; import { filters, getExportsForProps, waitForModule, waitForProps } from "../webpack"; +type StringConcat = (...rest: string[]) => string; + const ConstantsCommon = await waitForModule>(filters.bySource("BASE_URL:")); const Constants = await waitForModule>(filters.bySource("USER_PROFILE:")); export const raw = virtualMerge(ConstantsCommon, Constants); @@ -42,7 +44,7 @@ export const ChannelTypes = getExportsForProps>( "DM", "GUILD_FORUM", ])!; -export const Endpoints = getExportsForProps>(Constants, [ +export const Endpoints = getExportsForProps>(Constants, [ "USERS", "INTEGRATIONS", ])!; @@ -50,8 +52,15 @@ export const GuildFeatures = getExportsForProps>(Constant "VERIFIED", "ANIMATED_BANNER", ])!; -export const Routes = getExportsForProps>(Constants, ["INDEX", "LOGIN"])!; -export const UserFlags = getExportsForProps>(Constants, [ +export const MessageFlags = getExportsForProps>(Constants, [ + "EPHEMERAL", + "LOADING", +])!; +export const Routes = getExportsForProps>(Constants, [ + "INDEX", + "LOGIN", +])!; +export const UserFlags = getExportsForProps>(Constants, [ "STAFF", "SPAMMER", ])!; diff --git a/src/renderer/modules/common/flux.ts b/src/renderer/modules/common/flux.ts index 331000e61..281d321fb 100644 --- a/src/renderer/modules/common/flux.ts +++ b/src/renderer/modules/common/flux.ts @@ -9,9 +9,9 @@ interface Action { type: ActionType; } -type ActionHandler = (action: A) => void; +export type ActionHandler = (action: A) => boolean | void; -type ActionHandlerRecord = { +export type ActionHandlerRecord = { [A in ActionType]: ActionHandler<{ type: A; [key: string]: any }>; }; @@ -43,7 +43,7 @@ export declare class Emitter { type Callback = () => void; -declare class Callbacks { +declare class ChangeListeners { public listeners: Set; public add(listener: Callback): void; public remove(listener: Callback): void; @@ -55,7 +55,11 @@ declare class Callbacks { } export declare class Store { - public constructor(dispatcher: Dispatcher, actions?: ActionHandlerRecord, band?: DispatchBand); + public constructor( + dispatcher: Dispatcher, + actionHandler?: ActionHandlerRecord, + band?: DispatchBand, + ); public static destroy(): void; public static getAll(): Store[]; @@ -65,8 +69,8 @@ export declare class Store { public _isInitialized: boolean; public _dispatchToken: DispatchToken; public _dispatcher: Dispatcher; - public _changeCallbacks: Callbacks; - public _reactChangeCallbacks: Callbacks; + public _changeCallbacks: ChangeListeners; + public _reactChangeCallbacks: ChangeListeners; public _mustEmitChanges: Parameters[0]; public initialize(): void; @@ -75,8 +79,8 @@ export declare class Store { public getName(): string; public emitChange(): void; - public mustEmitChanges(func?: (action?: Action) => boolean): void; - public syncWith(stores: Store[], func: () => boolean, timeout?: number): void; + public mustEmitChanges(actionHandler?: ActionHandler): void; + public syncWith(stores: Store[], callback: () => boolean, timeout?: number): void; public waitFor(...stores: Store[]): void; public addChangeListener(listener: Callback): void; @@ -85,7 +89,7 @@ export declare class Store { public removeChangeListener(listener: Callback): void; public removeReactChangeListener(listener: Callback): void; - public registerActionHandlers(actions: ActionHandlerRecord, band?: DispatchBand): void; + public registerActionHandlers(actionHandlers: ActionHandlerRecord, band?: DispatchBand): void; // eslint-disable-next-line @typescript-eslint/naming-convention public __getLocalVars?(): Record; @@ -175,7 +179,7 @@ export declare class SnapshotStore> extends Store public clear: () => void; public getClass: () => any; public readSnapshot: (version: number) => Snapshot["data"] | null; - public registerActionHandlers: (actions: ActionHandlerRecord) => void; + public registerActionHandlers: (actionHandlers: ActionHandlerRecord) => void; public save: () => void; } diff --git a/src/renderer/modules/common/fluxDispatcher.ts b/src/renderer/modules/common/fluxDispatcher.ts index 232fec2f2..9c2eda770 100644 --- a/src/renderer/modules/common/fluxDispatcher.ts +++ b/src/renderer/modules/common/fluxDispatcher.ts @@ -1,5 +1,7 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import type EventEmitter from "events"; import { waitForProps } from "../webpack"; +import type { ActionHandler, ActionHandlerRecord } from "./flux"; export enum DispatchBand { Early, @@ -7,11 +9,9 @@ export enum DispatchBand { Default, } -type FluxCallback = (action?: { [index: string]: unknown }) => void; - interface Action { type: string; - [index: string]: unknown; + [key: string]: any; } type ActionMetric = [string, string, number]; @@ -57,7 +57,7 @@ declare class ActionLog { } interface NodeData { - actionHandler: Record; + actionHandler: ActionHandlerRecord; band: DispatchBand; name: string; storeDidChange: (action: Action) => void; @@ -86,12 +86,12 @@ export declare class DepGraph { } interface Handler { - actionHandler: FluxCallback; + actionHandler: ActionHandler; name: string; storeDidChange: (action: Action) => void; } -declare class ActionHandlers { +declare class ActionHandlersGraph { public _orderedActionHandlers: Record; public _orderedCallbackTokens: string[]; public _lastID: number; @@ -102,7 +102,7 @@ declare class ActionHandlers { public getOrderedActionHandlers: (action: Action) => Handler[]; public register: ( name: string, - actionHandler: Record, + actionHandlers: ActionHandlerRecord, storeDidChange: (action: Action) => void, band: DispatchBand, token?: string, @@ -117,12 +117,12 @@ declare class ActionHandlers { } export interface FluxDispatcher { - _actionHandlers: ActionHandlers; + _actionHandlers: ActionHandlersGraph; _currentDispatchActionType: string | null; _defaultBand: DispatchBand; _interceptors: Array<(...rest: unknown[]) => unknown>; _processingWaitQueue: boolean; - _subscriptions: Record>; + _subscriptions: Record>; _waitQueue: Array<(...rest: unknown[]) => unknown>; actionLogger: ActionLogger; @@ -139,12 +139,12 @@ export interface FluxDispatcher { dispatch: (action: Action) => void; flushWaitQueue: () => void; isDispatching: () => boolean; - subscribe: (type: string, callback: FluxCallback) => void; - unsubscribe: (type: string, callback: FluxCallback) => void; + subscribe: (type: string, callback: ActionHandler) => void; + unsubscribe: (type: string, callback: ActionHandler) => void; wait: (callback: (...rest: unknown[]) => unknown) => void; register: ( name: string, - actionHandler: Record, + actionHandlers: ActionHandlerRecord, storeDidChange: (action: Action) => void, band: DispatchBand, token?: string, diff --git a/src/renderer/modules/common/i18n.ts b/src/renderer/modules/common/i18n.ts index 93bb8e682..00a004de9 100644 --- a/src/renderer/modules/common/i18n.ts +++ b/src/renderer/modules/common/i18n.ts @@ -181,7 +181,7 @@ interface IntlMessageObject { plainFormat: (values?: string | IntlMessageValues) => string; } -type Message = string & IntlMessageObject; +export type Message = string & IntlMessageObject; type Messages = Record; export interface I18n extends EventEmitter { diff --git a/src/renderer/modules/common/messages.ts b/src/renderer/modules/common/messages.ts index c706bd05d..c09ee8ec1 100644 --- a/src/renderer/modules/common/messages.ts +++ b/src/renderer/modules/common/messages.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { waitForProps } from "../webpack"; - -import type { Channel, Message, MessageAttachment } from "discord-types/general"; +import type { Channel, Message, MessageAttachment, User } from "discord-types/general"; import { virtualMerge } from "src/renderer/util"; +import type { APIEmbed } from "src/types"; +import { filters, getFunctionBySource, waitForModule, waitForProps } from "../webpack"; export enum ActivityActionTypes { JOIN = 1, @@ -371,6 +371,12 @@ export interface MessageActions { ) => Promise; sendBotMessage: (channelId: string, content: string, messageName?: string) => void; sendClydeError: (channelId: string, code?: number) => void; + sendClydeProfileOverride: ( + channelId: string, + clydeProfileURL: string, + analyticsTriggeredFrom?: string, + suggestedInvite?: InviteSuggestion, + ) => Promise; sendGreetMessage: ( channelId: string, stickerId: string, @@ -414,14 +420,58 @@ export interface MessageActions { _tryFetchMessagesCached: (options: FetchMessagesCachedOptions) => boolean; } -export type Messages = PartialMessageStore & MessageActions; +interface CreateBotMessageOptions { + channelId: string; + content: string; + embeds?: APIEmbed[]; + loggingName?: string; +} + +interface CreateMessageOptions { + channelId: string; + content: string; + tts?: boolean; + type?: number; + messageReference?: MessageReference; + allowedMentions?: AllowedMentions; + author: User; + flags?: number; + nonce?: string; +} + +interface UserServer { + id: string; + username: string; + avatar: string | null; + discriminator: string; + bot: boolean | undefined; + global_name: string | null; +} + +interface MessageUtils { + createBotMessage: (options: CreateBotMessageOptions) => Message; + createMessage: (options: CreateMessageOptions) => Message; + createNonce: () => string; + userRecordToServer: (user: User) => UserServer; +} const MessageStore = await waitForProps("getMessage", "getMessages"); +const MessageUtilsMod = await waitForModule(filters.bySource('username:"Clyde"')); +const MessageUtils = { + createBotMessage: getFunctionBySource(MessageUtilsMod, 'username:"Clyde"'), + createMessage: getFunctionBySource(MessageUtilsMod, "createMessage"), + createNonce: getFunctionBySource(MessageUtilsMod, "fromTimestamp"), + userRecordToServer: getFunctionBySource(MessageUtilsMod, "global_name:"), +} as MessageUtils; + +export type Messages = PartialMessageStore & MessageActions & MessageUtils; + export default virtualMerge( await waitForProps("sendMessage", "editMessage", "deleteMessage"), { getMessage: MessageStore.getMessage, getMessages: MessageStore.getMessages, }, + MessageUtils, ); diff --git a/src/renderer/modules/components/ErrorBoundary.css b/src/renderer/modules/components/ErrorBoundary.css index 217456682..be2f0294d 100644 --- a/src/renderer/modules/components/ErrorBoundary.css +++ b/src/renderer/modules/components/ErrorBoundary.css @@ -4,7 +4,18 @@ font-weight: bold; color: var(--header-primary); text-align: center; - margin-bottom: 24px; + margin-bottom: 16px; +} + +.replugged-error-boundary h3 { + font-size: 14px; + font-family: var(--font-primary); + font-weight: 500; + color: var(--header-primary); +} + +.replugged-error-boundary .replugged-error-boundary-plugin { + margin-bottom: 8px; } .replugged-error-boundary p { @@ -15,3 +26,35 @@ text-align: center; margin: 0px; } + +.replugged-error-boundary-collapsible { + color: var(--header-primary); + background-color: var(--background-secondary-alt); + border-radius: 8px; + transition: background-color 0.1s ease; + padding: 8px 12px; + margin-top: 16px; +} + +.replugged-error-boundary-collapsible:active { + background-color: var(--background-accent); +} + +.replugged-error-boundary-collapsible-header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + cursor: pointer; +} + +.replugged-error-boundary-collapsible-stack { + background-color: var(--background-secondary); + color: var(--text-normal); + border-radius: 5px; + border: 1px solid var(--background-tertiary); + margin: 10px 0px; + padding: 12px; + user-select: text; + white-space: pre-wrap; +} diff --git a/src/renderer/modules/components/ErrorBoundary.tsx b/src/renderer/modules/components/ErrorBoundary.tsx index 6d9c7af7d..035a902ff 100644 --- a/src/renderer/modules/components/ErrorBoundary.tsx +++ b/src/renderer/modules/components/ErrorBoundary.tsx @@ -1,20 +1,54 @@ import { Messages } from "@common/i18n"; import React from "@common/react"; +import { plugins } from "@replugged"; import { Logger } from "../logger"; import "./ErrorBoundary.css"; const logger = new Logger("Components", "ErrorBoundary"); +function CollapsibleErrorStack(props: { stack: string }): React.ReactElement { + const { stack } = props; + + const [open, setOpen] = React.useState(false); + + const message = stack.split("\n")[0]; + + return ( +
+
setOpen(!open)}> +

{message}

+ + + +
+
+
+ {stack} +
+
+
+ ); +} + export interface ErrorProps { children: React.ReactNode; silent?: boolean; - onError?: (error: unknown, errorInfo: unknown) => void; + onError?: (error: Error, errorInfo: React.ErrorInfo) => void; /** Element to show if the error boundary is triggered */ fallback?: React.ReactNode; } export interface ErrorState { hasError: boolean; + error?: Error; + pluginName?: string; } export type ErrorBoundaryType = React.ComponentClass; @@ -25,11 +59,22 @@ export default class ErrorBoundary extends React.Component

{Messages.REPLUGGED_SETTINGS_ERROR_HEADER}

+ {pluginName && ( +

+ {Messages.REPLUGGED_SETTINGS_ERROR_PLUGIN_NAME?.format?.({ name: pluginName })} +

+ )}

{Messages.REPLUGGED_SETTINGS_ERROR_SUB_HEADER}

+ {error?.stack && ( + }> + + + )} ) ); diff --git a/src/renderer/modules/components/FormItem.tsx b/src/renderer/modules/components/FormItem.tsx index 6394fe1c3..118e0c8cb 100644 --- a/src/renderer/modules/components/FormItem.tsx +++ b/src/renderer/modules/components/FormItem.tsx @@ -40,25 +40,22 @@ export type FormItemType = React.FC; export default ((props) => { const { note, notePosition = "before", noteStyle, noteClassName, divider, ...compProps } = props; + + const noteStyleDefault = notePosition === "before" ? { marginBottom: 8 } : { marginTop: 8 }; + const noteComp = ( + + {note} + + ); + return ( - {note && notePosition === "before" && ( - - {note} - - )} + {note && notePosition === "before" && noteComp} {props.children} - {note && notePosition === "after" && ( - - {note} - - )} + {note && notePosition === "after" && noteComp} {divider && } ); diff --git a/src/renderer/modules/components/Loader.tsx b/src/renderer/modules/components/Loader.tsx index 011d23cd0..64cd51d65 100644 --- a/src/renderer/modules/components/Loader.tsx +++ b/src/renderer/modules/components/Loader.tsx @@ -6,6 +6,7 @@ const Types = { CHASING_DOTS: "chasingDots", PULSING_ELLIPSIS: "pulsingEllipsis", SPINNING_CIRCLE: "spinningCircle", + SPINNING_CIRCLE_SIMPLE: "spinningCircleSimple", LOW_MOTION: "lowMotion", } as const; @@ -20,7 +21,7 @@ type LoaderProps = GenericLoaderProps & { type?: (typeof Types)[keyof typeof Types]; } & React.ComponentPropsWithoutRef<"span">; type SpinningCircleLoaderProps = GenericLoaderProps & { - type?: (typeof Types)["SPINNING_CIRCLE"]; + type?: (typeof Types)["SPINNING_CIRCLE"] | (typeof Types)["SPINNING_CIRCLE_SIMPLE"]; } & React.ComponentPropsWithoutRef<"div">; export type LoaderType = React.FC & { diff --git a/src/renderer/modules/components/Modal.tsx b/src/renderer/modules/components/Modal.tsx index d278c5e9b..d61670a93 100644 --- a/src/renderer/modules/components/Modal.tsx +++ b/src/renderer/modules/components/Modal.tsx @@ -17,6 +17,7 @@ interface ModalRootProps extends Omit, "ch fullscreenOnMobile?: boolean; hideShadow?: boolean; onAnimationEnd?(): string; + returnRef?: React.Ref; } interface ModalHeaderProps { diff --git a/src/renderer/modules/components/RadioItem.tsx b/src/renderer/modules/components/RadioItem.tsx index 3dfdfddf5..1361fd34b 100644 --- a/src/renderer/modules/components/RadioItem.tsx +++ b/src/renderer/modules/components/RadioItem.tsx @@ -16,8 +16,7 @@ type RadioOptionType = { collapsibleContent?: React.ReactNode; }; -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -type RadioProps = { +interface RadioProps { options: RadioOptionType[]; value?: string; onChange: (option: RadioOptionType) => void; @@ -32,7 +31,7 @@ type RadioProps = { itemTitleClassName?: string; radioItemClassName?: string; collapsibleClassName?: string; -}; +} export type RadioType = React.FC & { Sizes: Record<"NOT_SET" | "NONE" | "SMALL" | "MEDIUM", string>; diff --git a/src/renderer/modules/components/SliderItem.tsx b/src/renderer/modules/components/SliderItem.tsx index de86a11dd..9b1ace67c 100644 --- a/src/renderer/modules/components/SliderItem.tsx +++ b/src/renderer/modules/components/SliderItem.tsx @@ -1,6 +1,6 @@ import type React from "react"; import { FormItem } from "."; -import { filters, waitForModule } from "../webpack"; +import { filters, waitForModule, waitForProps } from "../webpack"; const MarkerPositions = { ABOVE: 0, @@ -61,6 +61,8 @@ export const Slider = ((props) => { }) as SliderType; Slider.MarkerPositions = MarkerPositions; +const classes = await waitForProps>("marginTop20"); + interface SliderItemProps extends SliderProps { note?: string; style?: React.CSSProperties; @@ -69,7 +71,7 @@ interface SliderItemProps extends SliderProps { export type SliderItemType = React.FC>; export const SliderItem = (props: React.PropsWithChildren): React.ReactElement => { - const { children, ...compProps } = props; + const { children, className, ...compProps } = props; return ( ): Rea noteStyle={{ marginBottom: props.markers ? 16 : 4 }} disabled={props.disabled} divider> - + ); }; diff --git a/src/renderer/modules/injector.ts b/src/renderer/modules/injector.ts index 06169ef3f..8be38446b 100644 --- a/src/renderer/modules/injector.ts +++ b/src/renderer/modules/injector.ts @@ -1,9 +1,12 @@ -import type { ObjectExports } from "../../types/webpack"; -import type { AnyFunction } from "../../types/util"; -import type { GetButtonItem } from "../../types/coremods/message"; +import type { CommandOptions } from "../../types/discord"; +import type { RepluggedCommand } from "../../types/coremods/commands"; import type { ContextMenuTypes, GetContextItem } from "../../types/coremods/contextMenu"; -import { addButton } from "../coremods/messagePopover"; +import type { GetButtonItem } from "../../types/coremods/message"; +import type { AnyFunction } from "../../types/util"; +import type { ObjectExports } from "../../types/webpack"; +import { CommandManager } from "../apis/commands"; import { addContextMenuItem } from "../coremods/contextMenu"; +import { addButton } from "../coremods/messagePopover"; enum InjectionTypes { Before, @@ -217,7 +220,7 @@ function after< */ export class Injector { #uninjectors = new Set<() => void>(); - + #slashCommandManager = new CommandManager(); /** * Run code before a native module * @param obj Module to inject to @@ -361,6 +364,37 @@ export class Injector { this.#uninjectors.add(uninjector); return uninjector; }, + + /** + * A utility function to add a custom slash command. + * @param cmd The slash command to add to register + * @returns A callback to de-register the command + * + * @example + * ``` + * import { Injector, components, types } from "replugged"; + * + * const injector = new Injector(); + * + * export function start() { + * injector.utils.registerSlashCommand({ + * name: "use", + * description: "a command meant to be used", + * usage: "/use", + * executor: (interaction) => {}, + * }) + * } + * + * export function stop() { + * injector.uninjectAll(); + * } + * ``` + */ + registerSlashCommand: (cmd: RepluggedCommand) => { + const uninjector = this.#slashCommandManager.registerCommand(cmd); + this.#uninjectors.add(uninjector); + return uninjector; + }, }; /** diff --git a/src/renderer/modules/logger.ts b/src/renderer/modules/logger.ts index d9202d971..8a0ad0670 100644 --- a/src/renderer/modules/logger.ts +++ b/src/renderer/modules/logger.ts @@ -2,7 +2,14 @@ const blurple = "#5865F2"; -export type LoggerType = "Plugin" | "Coremod" | "API" | "Ignition" | "CommonModules" | "Components"; +export type LoggerType = + | "Plugin" + | "Coremod" + | "API" + | "Ignition" + | "CommonModules" + | "Components" + | "Manager"; const repluggedPrefix = (type: LoggerType, name: string): string => `%c[Replugged:${type}:${name}]`; diff --git a/src/renderer/replugged.ts b/src/renderer/replugged.ts index baa5ee924..534e0d7ec 100644 --- a/src/renderer/replugged.ts +++ b/src/renderer/replugged.ts @@ -22,7 +22,6 @@ export * as components from "./modules/components"; export * as i18n from "./modules/i18n"; export { default as notices } from "./apis/notices"; -export { default as commands } from "./apis/commands"; export * as settings from "./apis/settings"; /** diff --git a/src/types/addon.ts b/src/types/addon.ts index c95134ffb..b29ce9751 100644 --- a/src/types/addon.ts +++ b/src/types/addon.ts @@ -52,7 +52,7 @@ export interface RepluggedEntity { export const theme = common.extend({ type: z.literal("replugged-theme"), - main: z.string(), + main: z.string().optional(), splash: z.string().optional(), }); diff --git a/src/types/coremods/commands.ts b/src/types/coremods/commands.ts new file mode 100644 index 000000000..95349f84f --- /dev/null +++ b/src/types/coremods/commands.ts @@ -0,0 +1,98 @@ +import type { Channel, Guild } from "discord-types/general"; +import type { ValueOf } from "type-fest"; +import { CommandInteraction } from "../../renderer/apis/commands"; +import type { + APIEmbed, + ApplicationCommandOptionType, + CommandChoices, + CommandOptionReturn, + CommandOptions, + StringOptions, +} from "../discord"; + +interface OptionTypeMapping { + [ApplicationCommandOptionType.String]: string; + [ApplicationCommandOptionType.Integer]: number; + [ApplicationCommandOptionType.Boolean]: boolean; + [ApplicationCommandOptionType.User]: string; // its user id + [ApplicationCommandOptionType.Channel]: string; // its channel id + [ApplicationCommandOptionType.Role]: string; // its role id + [ApplicationCommandOptionType.Mentionable]: string; // id of whatever can be mentioned. usually channel/user/role + [ApplicationCommandOptionType.Number]: number; + [ApplicationCommandOptionType.Attachment]: { uploadedFilename: string; file: File }; +} + +type GetConditionallyOptional = Required extends true + ? T + : T | undefined; + +type GetType = GetConditionallyOptional< + T extends StringOptions + ? T["choices"] extends readonly CommandChoices[] + ? T["choices"][number]["value"] + : OptionTypeMapping[T["type"]] + : OptionTypeMapping[T["type"]], + T["required"] +>; + +export type GetCommandOption = Extract< + T, + { name: K } +>; + +export type GetCommandOptions = ValueOf<{ + [K in T["name"]]: { + focused?: boolean; + name: K; + type: T["type"]; + value: GetType>; + }; +}>; + +export type GetValueType = undefined extends T["value"] + ? Exclude | D + : T["value"]; + +export interface InexecutableRepluggedCommand { + applicationId?: string; + type?: number; + id?: string; + name: string; + displayName?: string; + description: string; + displayDescription?: string; + usage?: string; + options?: readonly T[]; +} + +export type RepluggedCommand = InexecutableRepluggedCommand & + ( + | { + executor: ( + interaction: CommandInteraction>, + ) => Promise | RepluggedCommandResult; + execute?: never; + } + | { + execute: ( + args: Array>, + currentInfo: { channel: Channel; guild: Guild }, + ) => Promise | void; + executor?: never; + } + ); + +export type AnyRepluggedCommand = RepluggedCommand; + +export type RepluggedCommandResult = { + send?: boolean; + result?: string | null; + embeds?: APIEmbed[]; +} | null; + +export interface RepluggedCommandSection { + id: string; + name: string; + type?: 1; + icon: string; +} diff --git a/src/types/discord.ts b/src/types/discord.ts index 791c24649..4eedc3783 100644 --- a/src/types/discord.ts +++ b/src/types/discord.ts @@ -1,26 +1,148 @@ -export interface CommandOptions { - type: number; +import { Message } from "@common/i18n"; + +export enum ApplicationCommandOptionType { + //Subcommand = 1, + //SubcommandGroup = 2, + String = 3, + Integer = 4, + Boolean = 5, + User = 6, + Channel = 7, + Role = 8, + Mentionable = 9, + Number = 10, + Attachment = 11, +} + +interface BaseCommandOptions { + type: T; name: string; displayName?: string; description: string; displayDescription?: string; + serverLocalizedName?: string; required?: boolean; - choices?: Array<{ - name: string; - values: string | number; - }>; - options?: CommandOptions[]; +} + +export interface CommandChoices { + name: string | Message; + displayName: string | Message; + value: string | number; +} + +export interface CommandOptionAutocompleteAndChoices { + autocomplete?: boolean; + choices?: readonly CommandChoices[]; + focused?: boolean; +} + +export interface StringOptions + extends CommandOptionAutocompleteAndChoices, + BaseCommandOptions { + /* eslint-disable @typescript-eslint/naming-convention */ + min_length?: number; + max_length?: number; + /* eslint-enable @typescript-eslint/naming-convention */ +} + +export interface NumberOptions + extends CommandOptionAutocompleteAndChoices, + BaseCommandOptions { /* eslint-disable @typescript-eslint/naming-convention */ - channel_types?: number[]; min_value?: number; max_value?: number; /* eslint-enable @typescript-eslint/naming-convention */ - autocomplete?: boolean; } +export interface ChannelOptions extends BaseCommandOptions { + /* eslint-disable @typescript-eslint/naming-convention */ + channel_types?: readonly number[]; +} + +export interface OtherCommandOptions + extends BaseCommandOptions< + | ApplicationCommandOptionType.Attachment + | ApplicationCommandOptionType.Boolean + | ApplicationCommandOptionType.Mentionable + | ApplicationCommandOptionType.Role + | ApplicationCommandOptionType.User + > {} + +export interface CommandOptionReturn { + name: string; + type: ApplicationCommandOptionType; + value: T; +} + +export type CommandOptions = StringOptions | NumberOptions | ChannelOptions | OtherCommandOptions; + export interface ConnectedAccount { type: string; name: string; id: string; verified: boolean; } + +export enum MessageEmbedTypes { + IMAGE = "image", + VIDEO = "video", + LINK = "link", + ARTICLE = "article", + TWEET = "tweet", + RICH = "rich", + GIFV = "gifv", + APPLICATION_NEWS = "application_news", + AUTO_MODERATION_MESSAGE = "auto_moderation_message", + AUTO_MODERATION_NOTIFICATION = "auto_moderation_notification", + TEXT = "text", + POST_PREVIEW = "post_preview", + GIFT = "gift", + SAFETY_POLICY_NOTICE = "safety_policy_notice", +} + +export interface APIEmbed { + title?: string; + type?: MessageEmbedTypes; + description?: string; + url?: string; + timestamp?: string; + color?: number; + footer?: { + text: string; + icon_url?: string; + proxy_icon_url?: string; + }; + image?: { + url: string; + proxy_url?: string; + height?: number; + width?: number; + }; + thumbnail?: { + url: string; + proxy_url?: string; + width?: number; + height?: number; + }; + video?: { + url?: string; + proxy_url?: string; + height?: number; + width?: number; + }; + provider?: { + name?: string; + url?: string; + }; + author?: { + name: string; + url?: string; + icon_url?: string; + proxy_icon_url?: string; + }; + fields?: Array<{ + name: string; + value: string; + inline?: boolean; + }>; +} diff --git a/src/types/index.ts b/src/types/index.ts index 1bc3ebbaf..e614b2c92 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ import type { WebContents } from "electron"; -import type { CommandOptions, ConnectedAccount } from "./discord"; +import type { ConnectedAccount } from "./discord"; import type { PluginManifest, ThemeManifest } from "./addon"; export type RepluggedWebContents = WebContents & { @@ -47,14 +47,6 @@ export interface RepluggedAnnouncement { }; } -export interface RepluggedCommand { - name: string; - description: string; - usage: string; - executor: (args: unknown) => void; - options: CommandOptions; -} - export interface RepluggedConnection { type: string; name: string; @@ -95,3 +87,4 @@ export * from "./installer"; export * from "./coremods/message"; export * from "./coremods/settings"; export * from "./coremods/contextMenu"; +export * from "./coremods/commands"; diff --git a/src/types/util.ts b/src/types/util.ts index 6b9265990..c3629d001 100644 --- a/src/types/util.ts +++ b/src/types/util.ts @@ -2,9 +2,3 @@ export type AnyFunction = (...args: any[]) => unknown; export type UnknownFunction = (...args: unknown[]) => unknown; export type ObjectKey = { [K in keyof O]: O[K] extends T ? K : never }[keyof O & string]; - -export type ReactComponent

= React.ComponentType< - React.PropsWithChildren

> ->; - -export type ObjectWithProps

= Record; diff --git a/src/util.mts b/src/util.mts index 1fe485cd7..99a1f8a85 100644 --- a/src/util.mts +++ b/src/util.mts @@ -1,6 +1,8 @@ +import esbuild from "esbuild"; import { execSync } from "child_process"; import { chownSync, existsSync, mkdirSync, statSync, writeFileSync } from "fs"; -import { join } from "path"; +import path, { join } from "path"; +import chalk from "chalk"; const REPLUGGED_FOLDER_NAME = "replugged"; export const configPathFn = (): string => { @@ -79,3 +81,67 @@ if (!existsSync(QUICK_CSS_FILE)) { chownSync(QUICK_CSS_FILE, REAL_UID, REAL_GID); } } + +export const formatBytes = (bytes: number): string => { + if (bytes === 0) return "0b"; + + const k = 1024; + const dm = 1; + const sizes = ["b", "kb", "mb", "gb"]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`; +}; + +export const logBuildPlugin: esbuild.Plugin = { + name: "logBuild", + setup: (build) => { + let start: number; + + build.onStart(() => { + start = Date.now(); + }); + build.onEnd((result) => { + const time = Date.now() - start; + const files = result.metafile?.outputs || {}; + + const fileData = Object.entries(files) + .sort(([a], [b]) => { + const aIsMap = a.endsWith(".map"); + const bIsMap = b.endsWith(".map"); + if (aIsMap && !bIsMap) return 1; + if (!aIsMap && bIsMap) return -1; + + return 0; + }) + .map(([file, { bytes }]) => { + const { sep } = path; + const dirname = path.dirname(file); + const basename = path.basename(file); + + const coloredName = [dirname, sep, chalk.bold(basename)].join(""); + + const sizeText = formatBytes(bytes); + const isBigFile = bytes > Math.pow(1024, 2) && !file.endsWith(".map"); // 1mb + const coloredSize = isBigFile ? chalk.yellow(sizeText) : chalk.cyan(sizeText); + const suffix = isBigFile ? chalk.yellow(" ⚠️") : ""; + + return { + name: coloredName, + size: coloredSize, + suffix, + }; + }); + const maxNameLength = Math.max(...fileData.map(({ name }) => name.length)); + const maxSizeLength = Math.max(...fileData.map(({ size }) => size.length)); + + console.log(""); + fileData.forEach(({ name, size, suffix }) => { + console.log(` ${name.padEnd(maxNameLength + 1)} ${size.padStart(maxSizeLength)}${suffix}`); + }); + console.log(""); + + console.log(`⚡ ${chalk.green(`Done in ${time.toLocaleString()}ms`)}`); + }); + }, +}; diff --git a/tsconfig.json b/tsconfig.json index 6a2e2f36d..1130d2a66 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -112,5 +112,5 @@ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, - "exclude": ["dist", "docs"] + "exclude": ["dist", "docs", "bin.mjs"] }