diff --git a/.gitignore b/.gitignore index 41e1305..dbe7f58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ .venv -node_modules -.idea __pycache__/ *.pyc examples/* !examples/*.yaml !examples/*.png +.idea diff --git a/README.md b/README.md index cf88ca2..4337d74 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,3 @@ # MakeCode Arcade to App Convert your MakeCode Arcade games into a standalone offline executable! - -## Install - -### Dependencies - -* Python (earliest version tested with is 3.12) -* Node (earliest version tested with is v20) -* Rust (earliest version tested with is 1.86) if building with Tauri - -### Steps - -1. Clone repo. -2. Create virtual environment and install Python dependencies in [ - `requirements.txt`](requirements.txt) into it. -3. Install JavaScript dependencies with `yarn`. - -## Usage - -Quick start: - -1. Create a YAML configuration file. See - [`Racers to Electron.yaml`](examples/Racers%20to%20Electron.yaml) for an - example. -2. Run the script with `python src/main.py "examples/Racers to Electron.yaml"`. - -### Input - -A YAML configuration file is used. Check out the examples in the -[`examples`](examples) directory to see how it's used, as it's -self-explanatory. Pass it as the first positional argument to the script. - -### Output - -The script will print the path to the output directory, which changes depending -on the output specified. - -#### Static files - -If the output is set to `static`, you will get HTML, CSS, and JS files that you -can serve from a web server. (maybe on your own website?) The script will print -out something like this: - -```commandline -... -dist/assets/index-D3F42Pqm.js 225.78 kB │ gzip: 70.51 kB -✓ built in 532ms -Done in 1.60s. -2025-05-15 20:28:48,660 - __main__ - INFO - Static website files are at C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-website\dist -2025-05-15 20:28:48,660 - __main__ - INFO - Build finished -``` - -You will find the HTML, CSS, and JS files at the specified path, where -`index.html` is the entry point. - -#### Executable with Electron - -If the output is set to `electron`, you will get an executable using the -Electron framework. The script will print out something like this: - -```commandline -... -› Artifacts available at: C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-electron\out\make -√ Running postMake hook -Done in 50.25s. -2025-05-15 20:32:22,652 - __main__ - INFO - Electron app executables are at C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-electron\out -2025-05-15 20:32:22,652 - __main__ - INFO - Build finished -``` - -You will find the directory with the executable and supporting files at the -specified path. (all of those files alongside the executable are needed, so -it's recommended to zip up the entire directory to distribute) You will also -find an installer in the `make` subdirectory. - -#### Executable with Tauri (recommended option) - -> This is the recommended option due to the small size and standalone nature of -> the executable. (~10 mb single executable with Tauri compared to almost 300 -> mb -> in files with Electron) - -If the output is set to `tauri`, you will get an executable using the Tauri -framework. The script will print out something like this: - -```commandline -... - C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-tauri\src-tauri\target\release\bundle\nsis\Racers v1.3.2_1.3.2_x64-setup.exe - -Done in 37.90s. -2025-05-15 23:39:14,612 - __main__ - INFO - Tauri app executables are at C:\Users\ckyiu\Documents\Projects\MakeCode-Arcade-to-App\examples\Racers\racers-tauri\src-tauri\target\release -2025-05-15 23:39:14,612 - __main__ - INFO - Build finished -``` - -You will find the executable at the specified path. (only the executable is -needed, no supporting files necessary) You will also find installers in the -`bundle` subdirectory. diff --git a/examples/Racers to Electron.yaml b/examples/Racers to Electron.yaml deleted file mode 100644 index 60df016..0000000 --- a/examples/Racers to Electron.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Example configuration file for MakeCode Arcade to App to compile to an executable - -name: Racers -description: "Enjoy the high-speed thrills of car racing in MakeCode Arcade! - For the MakeCode Arcade Mini Game Jam #3." -author: UnsignedArduino -version: 1.3.2 -# This is what the window title will be -# This will also be used for the executable file name for Electron outputs -# You can use {NAME} or {VERSION} or {AUTHOR} to substitute the correct values -title: "{NAME} v{VERSION}" - -# Game source - where the game code is located - this will download / copy everything -# Can be a GitHub repository (.git ending is optional) that specifies something to check out, -# a MakeCode Arcade share link, (ex. https://arcade.makecode.com/84426-33815-03715-00484) -# or a path (ex. F:/Racers) -#source: -# url: https://github.com/UnsignedArduino/Racers -# checkout: master # Could be a branch, tag, commit hash, etc. (it's passed to `git checkout` directly) -source: https://arcade.makecode.com/84426-33815-03715-00484 -#source: F:/Racers - -# Game icon - this will be the icon of the tab or executable -# Either absolute path to a file, a relative path from this file, or a URL -#icon: Racers icon.png -icon: https://raw.githubusercontent.com/UnsignedArduino/MakeCode-Arcade-to-App/refs/heads/main/examples/Racers%20icon.png - -# Output format - whether to output static files, an Electron app, or a Tauri app. -# output: static -output: electron -# output: tauri diff --git a/examples/Racers to Tauri.yaml b/examples/Racers to Tauri.yaml deleted file mode 100644 index dc2741a..0000000 --- a/examples/Racers to Tauri.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Example configuration file for MakeCode Arcade to App to compile to an executable - -name: Racers -description: "Enjoy the high-speed thrills of car racing in MakeCode Arcade! - For the MakeCode Arcade Mini Game Jam #3." -author: UnsignedArduino -version: 1.3.2 -# This is what the window title will be -# This will also be used for the executable file name for Electron outputs -# You can use {NAME} or {VERSION} or {AUTHOR} to substitute the correct values -title: "{NAME} v{VERSION}" - -# Game source - where the game code is located - this will download / copy everything -# Can be a GitHub repository (.git ending is optional) that specifies something to check out, -# a MakeCode Arcade share link, (ex. https://arcade.makecode.com/84426-33815-03715-00484) -# or a path (ex. F:/Racers) -#source: -# url: https://github.com/UnsignedArduino/Racers -# checkout: master # Could be a branch, tag, commit hash, etc. (it's passed to `git checkout` directly) -source: https://arcade.makecode.com/84426-33815-03715-00484 -#source: F:/Racers - -# Game icon - this will be the icon of the tab or executable -# Either absolute path to a file, a relative path from this file, or a URL -#icon: Racers icon.png -icon: https://raw.githubusercontent.com/UnsignedArduino/MakeCode-Arcade-to-App/refs/heads/main/examples/Racers%20icon.png - -# Output format - whether to output static files, an Electron app, or a Tauri app. -# output: static -# output: electron -output: tauri diff --git a/examples/Racers to static files.yaml b/examples/Racers to static files.yaml deleted file mode 100644 index 1e6443b..0000000 --- a/examples/Racers to static files.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Example configuration file for MakeCode Arcade to App to compile to an executable - -name: Racers -description: "Enjoy the high-speed thrills of car racing in MakeCode Arcade! - For the MakeCode Arcade Mini Game Jam #3." -author: UnsignedArduino -version: 1.3.2 -# This is what the window title will be -# This will also be used for the executable file name for Electron outputs -# You can use {NAME} or {VERSION} or {AUTHOR} to substitute the correct values -title: "{NAME} v{VERSION}" - -# Game source - where the game code is located - this will download / copy everything -# Can be a GitHub repository (.git ending is optional) that specifies something to check out, -# a MakeCode Arcade share link, (ex. https://arcade.makecode.com/84426-33815-03715-00484) -# or a path (ex. F:/Racers) -#source: -# url: https://github.com/UnsignedArduino/Racers -# checkout: master # Could be a branch, tag, commit hash, etc. (it's passed to `git checkout` directly) -source: https://arcade.makecode.com/84426-33815-03715-00484 -#source: F:/Racers - -# Game icon - this will be the icon of the tab or executable -# Either absolute path to a file, a relative path from this file, or a URL -#icon: Racers icon.png -icon: https://raw.githubusercontent.com/UnsignedArduino/MakeCode-Arcade-to-App/refs/heads/main/examples/Racers%20icon.png - -# Output format - whether to output static files, an Electron app, or a Tauri app. -output: static -# output: electron -# output: tauri diff --git a/examples/Racers to static.yaml b/examples/Racers to static.yaml new file mode 100644 index 0000000..9a1fa33 --- /dev/null +++ b/examples/Racers to static.yaml @@ -0,0 +1,61 @@ +# Example configuration file for MakeCode Arcade to App to compile to static +# HTML, CSS, and JS files + +# Config version (single number, incremented if breaking change to schema is +# made) +version: 1 + +project: + name: Racers + path_friendly_name: racers + description: "Enjoy the high-speed thrills of car racing in MakeCode Arcade! + For the MakeCode Arcade Mini Game Jam #3." + author: UnsignedArduino + version: 1.3.2 + # This is what the window title will be + # This will also be used for the executable file name for Electron outputs + # You can use {NAME} or {VERSION} or {AUTHOR} to substitute the correct + # values + title: "{NAME} v{VERSION}" + +inputs: + code: + # Can be share_link, github, or path + type: share_link + value: https://arcade.makecode.com/84426-33815-03715-00484 + + # code: + # type: github + # value: https://github.com/UnsignedArduino/Racers + # checkout: master + + # code: + # type: path + # value: "E:/Racers" + + assets: + icon: + # Can be url or path + type: url + value: https://raw.githubusercontent.com/UnsignedArduino/MakeCode-Arcade-to-App/refs/heads/main/examples/Racers%20icon.png + +# icon: +# type: path +# value: "./examples/Racers icon.png" + +# Path where we +build_dir: "./examples/Racers-build" + +# You can have multiple outputs +# Currently available options are static, electron, and tauri +outputs: + - type: static +# - type: electron +# window: +# width: 640 +# height: 480 +# - type: tauri +# identifier: com.unsignedarduino.racers +# window: +# width: 640 +# height: 480 diff --git a/package.json b/package.json deleted file mode 100644 index 4dcf6e3..0000000 --- a/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dependencies": { - "makecode": "^1.3.4" - }, - "packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" -} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9664ae0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,25 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "mkcd2app" +version = "0.1.0" +description = "Convert MakeCode Arcade games into static websites, Electron, or Tauri apps" +readme = "README.md" +requires-python = ">=3.14" +# We are pinning redun because we monkey patch something +dependencies = [ + "pydantic", + "pyyaml", + "redun==0.44.1", + "requests", + "beautifulsoup4", + "pillow", +] + +[project.scripts] +mkcd2app = "mkcd2app.main:main" + +[tool.hatch.build.targets.wheel] +packages = ["src/mkcd2app"] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 437be0e..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pyyaml -requests -beautifulsoup4 -pillow diff --git a/src/convert/mkcd_to_website/config.py b/src/convert/mkcd_to_website/config.py deleted file mode 100644 index 2d01b3c..0000000 --- a/src/convert/mkcd_to_website/config.py +++ /dev/null @@ -1,135 +0,0 @@ -import logging -from dataclasses import dataclass -from enum import Enum -from pathlib import Path -from typing import Optional -from urllib.parse import urlparse - -import yaml - -from utils.logger import create_logger - -logger = create_logger(name=__name__, level=logging.INFO) - - -class SourceType(Enum): - GITHUB = "github" - SHARE_LINK = "share_link" - PATH = "path" - - -class IconSourceType(Enum): - PATH = "path" - URL = "url" - - -class OutputType(Enum): - STATIC = "static" - ELECTRON = "electron" - TAURI = "tauri" - - -@dataclass -class Config: - """ - Configuration class. - """ - name: str - description: str - author: str - version: str - title: str - - source: str - source_type: SourceType - source_checkout: Optional[str] = None # For GitHub sources - - icon: Optional[Path | str] = None - icon_source_type: Optional[IconSourceType] = None - - output: OutputType = OutputType.STATIC - - -# https://stackoverflow.com/a/36283503/10291933 -def is_valid_url(url, qualifying=('scheme', 'netloc')): - tokens = urlparse(url) - return all(getattr(tokens, qualifying_attr) - for qualifying_attr in qualifying) - - -def determine_source_type(source: str | dict) -> SourceType: - """ - Determines the source type based on the provided source string. - - :param source: The source string or dictionary. - :return: The SourceType enum value. - """ - # We could do more checks for validity / strictness here - if type(source) is not str: - # Should have url and checkout - return SourceType.GITHUB - elif is_valid_url(source): - return SourceType.SHARE_LINK - else: - return SourceType.PATH - - -def determine_icon_source_type(icon: str) -> IconSourceType: - """ - Determines the icon source type based on the provided icon string. - - :param icon: The icon string. - :return: The IconSourceType enum value. - """ - # We could do more checks for validity / strictness here - if is_valid_url(icon): - return IconSourceType.URL - else: - return IconSourceType.PATH - - -def parse_config(yaml_text: str, cwd: Path) -> Config: - """ - Parses the YAML configuration file and returns a Config object. - - :param yaml_text: The YAML configuration text. - :param cwd: The current working directory to resolve relative paths. - :return: A Config object. - """ - logger.debug(f"Parsing YAML configuration") - result = yaml.safe_load(yaml_text) - - src = result.get("source") - src_type = determine_source_type(src) - logger.debug(f"Determined source type for {src} is {src_type}") - src_checkout = None - if type(src) is not str: - src_checkout = src.get("checkout") - src = src.get("url") - logger.debug(f"Complex GitHub source detected - will checkout {src_checkout} " - f"for url {src}") - - icon = result.get("icon") - icon_source_type = None if icon is None else determine_icon_source_type(icon) - if icon_source_type == IconSourceType.PATH: - icon = Path(icon) - if not icon.is_absolute(): - icon = cwd / icon - logger.debug(f"Determined icon source type for {icon} is {icon_source_type}") - - config = Config( - name=result.get("name"), - description=result.get("description"), - author=result.get("author"), - version=result.get("version"), - title=result.get("title"), - source=src, - source_type=src_type, - source_checkout=src_checkout, - icon=icon, - icon_source_type=icon_source_type, - output=OutputType(result.get("output", "static").lower()) - ) - config.title = config.title.format(NAME=config.name, VERSION=config.version, AUTHOR=config.author) - logger.debug(f"Parsed configuration: {config}") - return config diff --git a/src/convert/mkcd_to_website/source.py b/src/convert/mkcd_to_website/source.py deleted file mode 100644 index d5190c3..0000000 --- a/src/convert/mkcd_to_website/source.py +++ /dev/null @@ -1,67 +0,0 @@ -import logging -import shutil -from pathlib import Path -from typing import Optional - -from convert.mkcd_to_website.config import Config, SourceType -from utils.cmd import run_command, run_shell_command -from utils.logger import create_logger - -logger = create_logger(name=__name__, level=logging.INFO) - - -def download_source(config: Config, cwd: Path, - no_cache: Optional[bool] = False) -> Path: - """ - Downloads the source code based on the provided configuration. - - :param config: The configuration object containing source information. - :param cwd: The current working directory where the source code folder will be - downloaded. - :param no_cache: If True, forces a fresh download of the source code. - :return: The path to the downloaded source code. - """ - source_code_path = cwd / f"{config.name} source" - if no_cache: - logger.debug("Checking for existing source code to remove") - if source_code_path.exists(): - logger.debug(f"Removing {source_code_path}") - try: - shutil.rmtree(source_code_path) - except PermissionError as e: - logger.error(e) - logger.error("Permission denied. If it's a .git object, you may need " - "to delete it with admin/superuser privileges.") - raise e - elif source_code_path.exists(): - logger.debug(f"Source code already exists at {source_code_path}") - if config.source_type == SourceType.GITHUB: - logger.debug("Checking for updates") - run_command(["git", "checkout", config.source_checkout], - cwd=source_code_path) - run_command(["git", "pull"], cwd=source_code_path) - return source_code_path - if config.source_type == SourceType.GITHUB: - logger.info(f"Downloading source from GitHub") - # Assume it's `git clone`able - run_command(["git", "clone", config.source, source_code_path], cwd=cwd) - logger.info(f"Checking out {config.source_checkout}") - run_command(["git", "checkout", config.source_checkout], cwd=source_code_path) - elif config.source_type == SourceType.SHARE_LINK: - logger.info(f"Downloading source from share link") - source_code_path.mkdir(parents=True, exist_ok=True) - run_shell_command(f"npx mkc download {config.source}", cwd=source_code_path) - elif config.source_type == SourceType.PATH: - logger.info(f"Copying source from path") - source_code_path.mkdir(parents=True, exist_ok=True) - # Copy the contents of the source directory to the source_code_path - source_path = Path(config.source) - for item in source_path.iterdir(): - if item.is_dir(): - shutil.copytree(item, source_code_path / item.name) - else: - shutil.copy2(item, source_code_path / item.name) - else: - raise ValueError(f"Unknown source type {config.source_type}") - logger.debug(f"Source code path: {source_code_path}") - return source_code_path diff --git a/src/convert/mkcd_to_website/website.py b/src/convert/mkcd_to_website/website.py deleted file mode 100644 index 630c0df..0000000 --- a/src/convert/mkcd_to_website/website.py +++ /dev/null @@ -1,153 +0,0 @@ -import json -import logging -import shutil -from io import BytesIO -from pathlib import Path -from typing import Callable - -import requests -from PIL import Image -from bs4 import BeautifulSoup - -from convert.mkcd_to_website.config import Config, IconSourceType, SourceType -from utils.cmd import run_shell_command -from utils.logger import create_logger - -logger = create_logger(name=__name__, level=logging.INFO) - - -def generate_website(config: Config, prj_name: str, template_dir: Path, cwd: Path, - bin_js_path: Path): - """ - Generate the website by initializing a React TS Vite project, copying the necessary - files, and substituting the correct values in. - - :param config: The configuration object containing the project information. - :param prj_name: The name of the project. - :param template_dir: The directory containing the template files. - :param cwd: The current working directory where the project will be created. - :param bin_js_path: The path to the binary.js file. - """ - logger.debug(f"Creating React TS Vite project for {prj_name}") - # Initialize a React TS Vite project - if (cwd / prj_name).exists(): - logger.debug(f"Project {prj_name} already exists, continuing...") - else: - run_shell_command(f"yarn create vite {prj_name} -t react-ts --no-interactive --no-rolldown", cwd=cwd) - # Start copying files from template - old_dir = template_dir - new_dir = cwd / prj_name - - def copy_template(file_name: str, callback: Callable[[str], str] = lambda x: x): - (new_dir / file_name).write_text(callback((old_dir / file_name).read_text())) - - logger.debug(f"Copying website files from {old_dir} to {new_dir}") - # Copy index.html and substitute the title - copy_template("index.html", lambda x: x.format(TITLE=config.title)) - # Modify package.json - package_json = json.loads((old_dir / "package.json").read_text()) - package_json["name"] = prj_name - package_json["version"] = config.version - package_json["description"] = config.description - package_json["author"] = config.author - package_json["scripts"] = { - "dev": "vite", - "lint": "eslint .", - "writeLint": "eslint --fix .", - "format": "prettier --check .", - "writeFormat": "prettier --write .", - "build": "tsc -b && vite build", - "preview": "vite preview" - } - (new_dir / "package.json").write_text(json.dumps(package_json, indent=2)) - # Queue dependencies to add (these will be "yarn add"ed) - dependencies = ["react-toastify"] - dev_dependencies = ["eslint-plugin-react-dom", "eslint-plugin-react-x", "prettier"] - # Copy README.md - copy_template("README.md", - lambda x: x.format(WEBSITE_NAME=prj_name, - SOURCE=f"{config.source} @ {config.source_checkout}" if config.source_type == SourceType.GITHUB else config.source)) - # Copy more files - for file_name in ("vite.config.ts", "eslint.config.js", ".prettierignore", - "tsconfig.json", - "tsconfig.app.json", "tsconfig.node.json"): - copy_template(file_name) - # yarn - run_shell_command("yarn", cwd=new_dir) - # yarn add stuff - run_shell_command(f"yarn add {" ".join(dependencies)}", cwd=new_dir) - run_shell_command(f"yarn add {" ".join(dev_dependencies)} --dev", cwd=new_dir) - # Clear public directory - if (new_dir / "public").exists(): - shutil.rmtree(new_dir / "public") - (new_dir / "public").mkdir(parents=True, exist_ok=True) - # Clear src/assets directory - if (new_dir / "src" / "assets").exists(): - shutil.rmtree(new_dir / "src" / "assets") - # Copy src directory - shutil.copytree(old_dir / "src", new_dir / "src", dirs_exist_ok=True) - game_config_ts_path = new_dir / "src" / "gameConfiguration.ts" - logger.debug(f"gameConfiguration.ts at {game_config_ts_path}") - # Copy binary.js - logger.debug(f"Copying binary.js from {bin_js_path}") - shutil.copy(bin_js_path, new_dir / "public" / "binary.js") - # Download https://trg-arcade.userpxt.io/---simulator - logger.debug("Downloading simulator files") - res = requests.get("https://trg-arcade.userpxt.io/---simulator") - if res.ok: - sim_html = res.text - else: - raise Exception(f"Failed to download simulator: {res.status_code} {res.reason}") - # Analyze simulator HTML for required CSS and JS files - logger.debug("Analyzing simulator HTML for required CSS and JS files") - soup = BeautifulSoup(sim_html, features="html.parser") - css_links = soup.find_all("link", rel="stylesheet") - js_scripts = soup.find_all("script") - logger.debug(f"Found {len(css_links)} CSS links and {len(js_scripts)} JS scripts") - for css in css_links: - if css.get("href"): - css_url = css.get("href") - logger.debug(f"Downloading CSS file: {css_url}") - res = requests.get(css_url) - if res.ok: - file_name = css_url.split("/")[-1] - # Download CSS file - (new_dir / "public" / file_name).write_text(res.text) - # Rewrite CSS file to use relative paths - css["href"] = f"./{file_name}" - else: - raise Exception( - f"Failed to download CSS file: {res.status_code} {res.reason}") - for js in js_scripts: - if js.get("src"): - js_url = js.get("src") - logger.debug(f"Downloading JS file: {js_url}") - res = requests.get(js_url) - if res.ok: - file_name = js_url.split("/")[-1] - # Download JS file - (new_dir / "public" / file_name).write_text(res.text) - # Rewrite JS file to use relative paths - js["src"] = f"./{file_name}" - else: - raise Exception( - f"Failed to download JS file: {res.status_code} {res.reason}") - sim_html = soup.prettify(formatter="html5") - (new_dir / "public" / "---simulator.html").write_text(sim_html) - # Copy or download icon to favicon.ico in public directory - if config.icon: - logger.debug(f"Found icon to use") - if config.icon_source_type == IconSourceType.URL: - logger.debug(f"Downloading icon from {config.icon}") - res = requests.get(config.icon) - buffer = BytesIO(res.content) - if res.ok: - im = Image.open(buffer) - else: - raise Exception( - f"Failed to download icon: {res.status_code} {res.reason}") - else: - logger.debug(f"Reading icon from {config.icon}") - im = Image.open(config.icon) - logger.debug("Saving icon as favicon.ico") - im.save(new_dir / "public" / "favicon.ico") diff --git a/src/convert/website_to_electron/electron.py b/src/convert/website_to_electron/electron.py deleted file mode 100644 index 34d4e50..0000000 --- a/src/convert/website_to_electron/electron.py +++ /dev/null @@ -1,122 +0,0 @@ -import json -import logging -import shutil -from io import BytesIO -from pathlib import Path -from typing import Callable - -import requests -from PIL import Image - -from convert.mkcd_to_website.config import Config, IconSourceType, SourceType -from utils.cmd import run_shell_command -from utils.filesystem import copy_these, delete_these -from utils.logger import create_logger - -logger = create_logger(name=__name__, level=logging.INFO) - - -def get_icon(config: Config, src_dir: Path): - """ - Prepares the icon for the Electron app. If the icon is a URL, it downloads the icon - and saves it to the specified path. If the icon is a local file, it copies the file - to the specified path. Then it will convert it to ICO for Windows, ICNS for Mac, and - PNG for Linux. - - :param config: The configuration object containing the project information. - :param src_dir: The src directory of the Electron app - the icon will be saved here. - """ - if config.icon is None: - logger.debug("No icon specified, skipping icon generation.") - return - logger.debug(f"Preparing icon") - icon_dir = src_dir / "assets" / "icons" - icon_dir.mkdir(parents=True, exist_ok=True) - if config.icon_source_type == IconSourceType.PATH: - path = Path(config.icon) - source_icon_path = icon_dir / f"source{path.suffix}" - logger.debug(f"Copying icon from {path} to {source_icon_path}") - shutil.copy(path, source_icon_path) - else: - url = config.icon - logger.debug(f"Downloading icon from {url}") - res = requests.get(url) - if not res.ok: - logger.error(f"Failed to download icon from {url}") - raise Exception(f"Failed to download icon from {url}") - buffer = BytesIO(res.content) - logger.debug( - f"Downloaded image size {round(buffer.getbuffer().nbytes / 1024)} kb") - img = Image.open(buffer) - source_icon_path = icon_dir / f"source.png" - img.save(source_icon_path) - logger.debug(f"Saved icon to {source_icon_path}") - - formats = ["ico", "icns", "png"] - source_img = Image.open(source_icon_path) - for format in formats: - icon_path = icon_dir / f"icon.{format}" - logger.debug(f"Converting icon to {format} and saving to {icon_path}") - source_img.save(icon_path, format=format.upper(), - sizes=[(256, 256)] if format == "ico" else None) - - -def generate_electron(config: Config, prj_name: str, template_dir: Path, dist_dir: Path, - cwd: Path): - """ - Generate the Electron app from static HTML, CSS, and JS files. Assumes index.html - is the entry point. - - :param config: The configuration object containing the project information. - :param prj_name: The name of the project. - :param template_dir: The directory containing the template files. - :param dist_dir: The dist directory with all the static HTML, CSS, and JS files. - :param cwd: The current working directory where the project will be created. - """ - logger.debug(f"Creating Electron app for {prj_name}") - # Initialize an Electron project - prj_dir = cwd / prj_name - prj_src_dir = prj_dir / "src" - if prj_dir.exists(): - logger.debug(f"Project {prj_name} already exists, continuing...") - else: - run_shell_command( - f"npx --yes create-electron-app@latest {prj_name} --template=webpack", - cwd=cwd) - delete_these(["package-lock.json"], prj_dir) - delete_these(["index.html", "index.css"], prj_src_dir) - # Start copying files from template - old_dir = template_dir - new_dir = prj_dir - - def copy_template(file_name: str, callback: Callable[[str], str] = lambda x: x): - (new_dir / file_name).write_text(callback((old_dir / file_name).read_text())) - - logger.debug(f"Copying website files from {old_dir} to {new_dir}") - # Modify package.json - package_json = json.loads((old_dir / "package.json").read_text()) - package_json["name"] = prj_name - package_json["productName"] = config.title - package_json["version"] = config.version - package_json["description"] = config.description - package_json["author"] = config.author - (new_dir / "package.json").write_text(json.dumps(package_json, indent=2)) - # Copy README.md - copy_template("README.md", - lambda x: x.format(WEBSITE_NAME=prj_name, - SOURCE=f"{config.source} @ {config.source_checkout}" if config.source_type == SourceType.GITHUB else config.source)) - # Copy forge.config.js, webpack.main.config.js, etc. - for file_name in ("forge.config.js", "webpack.main.config.js", - "webpack.renderer.config.js", "webpack.rules.js"): - copy_template(file_name) - # Copy src directory - copy_these(list([p.name for p in (old_dir / "src").glob("*")]), old_dir / "src", - prj_dir / "src") - # Copy dist directory - static_dir = prj_src_dir / "static" - static_dir.mkdir(parents=True, exist_ok=True) - copy_these(list([p.name for p in dist_dir.glob("*")]), dist_dir, static_dir) - # Get icons - get_icon(config, prj_src_dir) - # yarn - run_shell_command("yarn", cwd=new_dir) diff --git a/src/convert/website_to_tauri/tauri.py b/src/convert/website_to_tauri/tauri.py deleted file mode 100644 index 78af445..0000000 --- a/src/convert/website_to_tauri/tauri.py +++ /dev/null @@ -1,127 +0,0 @@ -import json -import logging -import shutil -from io import BytesIO -from pathlib import Path -from typing import Callable - -import requests -from PIL import Image - -from convert.mkcd_to_website.config import Config, IconSourceType, SourceType -from utils.cmd import run_shell_command -from utils.filesystem import copy_these, delete_these -from utils.logger import create_logger - -logger = create_logger(name=__name__, level=logging.INFO) - - -def get_icon(config: Config, src_dir: Path): - """ - Prepares the icon for the Electron app. If the icon is a URL, it downloads the icon - and saves it to the specified path. If the icon is a local file, it copies the file - to the specified path. Then it will convert it to ICO for Windows, ICNS for Mac, and - PNG for Linux. - - :param config: The configuration object containing the project information. - :param src_dir: The src-tauri directory of the Tauri app - the icon will be saved - here. - """ - if config.icon is None: - logger.debug("No icon specified, skipping icon generation.") - return - logger.debug(f"Preparing icon") - icon_dir = src_dir / "icons" - icon_dir.mkdir(parents=True, exist_ok=True) - if config.icon_source_type == IconSourceType.PATH: - path = Path(config.icon) - source_icon_path = icon_dir / f"source{path.suffix}" - logger.debug(f"Copying icon from {path} to {source_icon_path}") - shutil.copy(path, source_icon_path) - else: - url = config.icon - logger.debug(f"Downloading icon from {url}") - res = requests.get(url) - if not res.ok: - logger.error(f"Failed to download icon from {url}") - raise Exception(f"Failed to download icon from {url}") - buffer = BytesIO(res.content) - logger.debug( - f"Downloaded image size {round(buffer.getbuffer().nbytes / 1024)} kb") - img = Image.open(buffer) - source_icon_path = icon_dir / f"source.png" - img.save(source_icon_path) - logger.debug(f"Saved icon to {source_icon_path}") - - formats = ["ico", "icns", "png"] - source_img = Image.open(source_icon_path) - for format in formats: - icon_path = icon_dir / f"icon.{format}" - logger.debug(f"Converting icon to {format} and saving to {icon_path}") - source_img.save(icon_path, format=format.upper(), - sizes=[(256, 256)] if format == "ico" else None) - - -def generate_tauri(config: Config, prj_name: str, template_dir: Path, dist_dir: Path, - cwd: Path): - """ - Generate the Tauri app from static HTML, CSS, and JS files. - - :param config: The configuration object containing the project information. - :param prj_name: The name of the project. - :param template_dir: The directory containing the template files. - :param dist_dir: The dist directory with all the static HTML, CSS, and JS files. - :param cwd: The current working directory where the project will be created. - """ - logger.debug(f"Creating Tauri app for {prj_name}") - # Initialize a Tauri project - prj_dir = cwd / prj_name - prj_src_dir = prj_dir / "src" - if prj_dir.exists(): - logger.debug(f"Project {prj_name} already exists, continuing...") - else: - run_shell_command( - f"yarn create tauri-app {prj_name} -m yarn -t vanilla -y", - cwd=cwd) - delete_these([".vscode"], prj_dir) - delete_these(["assets", "index.html", "main.js", "style.css"], prj_src_dir) - # Start copying files from template - old_dir = template_dir - new_dir = prj_dir - - def copy_template(file_name: str, callback: Callable[[str], str] = lambda x: x): - (new_dir / file_name).write_text(callback((old_dir / file_name).read_text())) - - logger.debug(f"Copying website files from {old_dir} to {new_dir}") - # Modify package.json - package_json = json.loads((old_dir / "package.json").read_text()) - package_json["name"] = prj_name - package_json["version"] = config.version - package_json["description"] = config.description - package_json["author"] = config.author - (new_dir / "package.json").write_text(json.dumps(package_json, indent=2)) - # Copy README.md - copy_template("README.md", - lambda x: x.format(WEBSITE_NAME=prj_name, - SOURCE=f"{config.source} @ {config.source_checkout}" if config.source_type == SourceType.GITHUB else config.source)) - # Copy src-tauri directory - copy_these(list([p.name for p in (old_dir / "src-tauri").glob("*")]), - old_dir / "src-tauri", - prj_dir / "src-tauri") - # Modify src-tauri/tauri.conf.json - tauri_conf_json = json.loads((old_dir / "src-tauri/tauri.conf.json").read_text()) - tauri_conf_json["productName"] = config.title - tauri_conf_json["version"] = config.version - tauri_conf_json["identifier"] = f"com.{prj_name}.app" - tauri_conf_json["app"]["windows"][0]["title"] = config.title - tauri_conf_json["app"]["windows"][0]["width"] = 160 * 4 - tauri_conf_json["app"]["windows"][0]["height"] = 120 * 4 - (new_dir / "src-tauri" / "tauri.conf.json").write_text( - json.dumps(tauri_conf_json, indent=2)) - # Copy dist directory - prj_src_dir.mkdir(parents=True, exist_ok=True) - copy_these(list([p.name for p in dist_dir.glob("*")]), dist_dir, prj_src_dir) - # Get icons - get_icon(config, prj_dir / "src-tauri") - # yarn - run_shell_command("yarn", cwd=prj_dir) diff --git a/src/main.py b/src/main.py deleted file mode 100644 index f623106..0000000 --- a/src/main.py +++ /dev/null @@ -1,178 +0,0 @@ -import logging -from argparse import ArgumentParser -from pathlib import Path - -from convert.mkcd_to_website.config import OutputType, parse_config -from convert.mkcd_to_website.source import download_source -from convert.mkcd_to_website.website import generate_website -from convert.website_to_electron.electron import generate_electron -from convert.website_to_tauri.tauri import generate_tauri -from utils.cmd import run_shell_command -from utils.filesystem import delete_these -from utils.logger import create_logger, set_all_stdout_logger_levels - -logger = create_logger(name=__name__, level=logging.INFO) - -parser = ArgumentParser(description="Convert your MakeCode Arcade games into a " - "standalone offline executable!") -parser.add_argument("config_path", type=Path, - help="Path to the YAML configuration file.") -parser.add_argument("--no-cache", action="store_true", - help="Do not use cached files. This will delete and download all " - "necessary files.") -parser.add_argument("--skip-source-download", action="store_true", - help="Skip source code download. This is useful for debugging.") -parser.add_argument("--skip-bin-build", action="store_true", - help="Skip building the game binary. This is useful for debugging.") -parser.add_argument("--skip-website-gen", action="store_true", - help="Skip website generation. This is useful for debugging.") -parser.add_argument("--skip-website-build", action="store_true", - help="Skip building the website. This is useful for debugging.") -parser.add_argument("--skip-electron-gen", action="store_true", - help="Skip Electron app generation. This is useful for debugging.") -parser.add_argument("--skip-electron-build", action="store_true", - help="Skip building the Electron app. This is useful for debugging.") -parser.add_argument("--skip-tauri-gen", action="store_true", - help="Skip Tauri app generation. This is useful for debugging.") -parser.add_argument("--skip-tauri-build", action="store_true", - help="Skip building the Tauri app. This is useful for debugging.") -parser.add_argument("--debug", action="store_true", - help="Enable debug logging.") -args = parser.parse_args() -debug = bool(args.debug) -if debug: - set_all_stdout_logger_levels(logging.DEBUG) -logger.debug(f"Received arguments: {args}") - -config_path = Path(args.config_path) -logger.info(f"Loading configuration from {config_path}") -config = parse_config(config_path.read_text(), config_path.parent) - -output_format = config.output -logger.debug(f"Building to {output_format.value}") -logger.debug(f"Window title will be {config.title}") - -no_cache = bool(args.no_cache) -if no_cache: - logger.info("No cache option selected. Ignoring cached files.") -skip_source_download = bool(args.skip_source_download) -skip_bin_build = bool(args.skip_bin_build) -skip_website_gen = bool(args.skip_website_gen) -skip_website_build = bool(args.skip_website_build) -skip_electron_gen = bool(args.skip_electron_gen) -skip_electron_build = bool(args.skip_electron_build) -skip_tauri_gen = bool(args.skip_tauri_gen) -skip_tauri_build = bool(args.skip_tauri_build) - -cwd = config_path.parent / config.name -src_dir = Path(__file__).parent -logger.debug(f"Current working directory: {cwd} (source code directory will be " - f"downloaded here)") -logger.debug(f"Source code directory: {src_dir}") -cwd.mkdir(parents=True, exist_ok=True) - -# Download source code -if skip_source_download: - logger.info("Skipping source code download") - source_code_path = cwd / f"{config.name} source" -else: - logger.info("Downloading source code") - source_code_path = download_source(config, cwd, no_cache) - -# npx pxt build -binary_js_path = source_code_path / "built" / "binary.js" -if skip_bin_build: - logger.info("Skipping build") -else: - logger.info("Building project") - if no_cache: - logger.debug("Checking for binary to remove") - if binary_js_path.exists(): - logger.debug(f"Deleting {binary_js_path}") - binary_js_path.unlink() - run_shell_command("npx mkc build -j", cwd=source_code_path) -logger.debug(f"Binary JS path: {binary_js_path}") - -# yarn create vite, copy files, and substitute values -vite_project_name = f"{config.name.lower().replace(" ", "-")}-website" -website_path = cwd / vite_project_name -if skip_website_gen: - logger.info("Skipping website generation") -else: - logger.info(f"Generating TS React and Vite website") - if no_cache: - logger.debug("Checking for existing website to remove") - delete_these([vite_project_name], cwd) - logger.debug(f"Creating Vite project with name {vite_project_name}") - generate_website(config, vite_project_name, src_dir / "templates" / "website_files", - cwd, binary_js_path) - -# yarn run build -website_dist_path = website_path / "dist" -if skip_website_build: - logger.info("Skipping website build") -else: - logger.info("Building website") - run_shell_command("yarn build", cwd=website_path) - -logger.info(f"Static website files are at {website_dist_path}") -if output_format == OutputType.STATIC: - logger.info(f"Build finished") - exit(0) -elif output_format == OutputType.ELECTRON: - electron_project_name = f"{config.name.lower().replace(" ", "-")}-electron" - electron_path = cwd / electron_project_name - if skip_electron_gen: - logger.info("Skipping Electron app generation") - else: - logger.info(f"Generating Electron app") - logger.debug( - f"Creating Electron app in {cwd}, using {website_dist_path} for source") - logger.debug(f"Creating Electron project with name {electron_project_name}") - # npx create-electron-app@latest, copy files, and substitute values - if no_cache: - logger.debug("Checking for existing website to remove") - delete_these([electron_project_name], cwd) - generate_electron(config, electron_project_name, - src_dir / "templates" / "electron_files", website_dist_path, - cwd) - - # yarn run make - electron_dist_path = electron_path / "out" - if skip_electron_build: - logger.info("Skipping Electron app build") - else: - logger.info("Building Electron app") - run_shell_command("yarn run make", cwd=electron_path) - - logger.info(f"Electron app executables are at {electron_dist_path}") - logger.info(f"Build finished") - exit(0) -elif output_format == OutputType.TAURI: - tauri_project_name = f"{config.name.lower().replace(' ', '-')}-tauri" - tauri_path = cwd / tauri_project_name - if skip_tauri_gen: - logger.info("Skipping Tauri app generation") - else: - logger.info(f"Generating Tauri app") - logger.debug( - f"Creating Tauri app in {cwd}, using {website_dist_path} for source") - logger.debug(f"Creating Tauri project with name {tauri_project_name}") - # yarn create tauri-app - if no_cache: - logger.debug("Checking for existing website to remove") - delete_these([tauri_project_name], cwd) - generate_tauri(config, tauri_project_name, - src_dir / "templates" / "tauri_files", website_dist_path, cwd) - - # yarn run tauri build - tauri_dist_path = tauri_path / "src-tauri" / "target" / "release" - if skip_tauri_build: - logger.info("Skipping Tauri app build") - else: - logger.info("Building Tauri app") - run_shell_command("yarn run tauri build", cwd=tauri_path) - - logger.info(f"Tauri app executables are at {tauri_dist_path}") - logger.info(f"Build finished") - exit(0) diff --git a/src/convert/__init__.py b/src/mkcd2app/__init__.py similarity index 100% rename from src/convert/__init__.py rename to src/mkcd2app/__init__.py diff --git a/src/mkcd2app/build_project/__init__.py b/src/mkcd2app/build_project/__init__.py new file mode 100644 index 0000000..da676b7 --- /dev/null +++ b/src/mkcd2app/build_project/__init__.py @@ -0,0 +1,80 @@ +import logging +import shutil +from pathlib import Path + +from redun import task +from redun.file import ContentDir + +from mkcd2app.build_project.inputs.code import build_binary_js, \ + download_supporting_files, fetch_code +from mkcd2app.build_project.website import copy_website_template, \ + fill_website_template, install_website_dependencies +from mkcd2app.config import load_config_from_yaml +from mkcd2app.utils.logger import create_logger +from mkcd2app.utils.resources import get_js_tools_path +from mkcd2app.utils.run import run_cmd + +logger = create_logger(name=__name__, level=logging.INFO) + + +@task(namespace="mkcd2app") +def install_mkcd_build_tools(config_yaml: str) -> ContentDir: + """ + Installs the MakeCode Arcade build tools. + + :param config_yaml: The raw YAML text of the config file. + :return: A redun.ContentDir that points to node_modules, this is only used so that + redun will see that some tasks depend on `mkc` being installed. + """ + logger.info("Installing MakeCode Arcade build tools") + + config = load_config_from_yaml(config_yaml) + build_path = Path(config.build_dir) + logger.debug(f"Tools will be installed in {build_path}") + + with get_js_tools_path() as js_tools_path: + shutil.copy(js_tools_path / "package.json", build_path / "package.json") + shutil.copy(js_tools_path / "package-lock.json", + build_path / "package-lock.json") + + run_cmd("npm ci", cwd=build_path) + + logger.debug("All MakeCode Arcade build tools installed") + + return ContentDir(str(build_path / "node_modules")) + + +@task(namespace="mkcd2app") +def build_project(config_yaml: str): + """ + Build the project. + + :param config_yaml: The raw YAML text of the config file. + """ + logger.info("Building project") + + config = load_config_from_yaml(config_yaml) + build_dir = Path(config.build_dir) + logger.debug(f"Project build directory is at {build_dir}") + build_dir.mkdir(parents=True, exist_ok=True) + + # Install `mkc` with `npm ci` in build dir + node_modules_for_mkc = install_mkcd_build_tools(config_yaml) + # Fetch game source code with `mkc`, `git`, or copy from disk + code_path = fetch_code(config_yaml, node_modules_for_mkc) + # Build binary.js with `mkc` + bin_js_path = build_binary_js(config_yaml, code_path) + # Download supporting files to run binary.js, including ---simulator.html and all + # it's references + support_path = download_supporting_files(config_yaml) + + # Copy website template (clean copy with template files only) + website_path = copy_website_template(config_yaml) + # Copy + fill (separate dir so stages don't mutate each other's + # ContentDir/ContentPath + website_filled_path = fill_website_template(config_yaml, website_path, bin_js_path, + support_path) + # Copy + `npm ci` (separate dir for same reason) + website_filled_deps_path = install_website_dependencies(website_filled_path) + + return website_filled_deps_path diff --git a/src/convert/mkcd_to_website/__init__.py b/src/mkcd2app/build_project/inputs/__init__.py similarity index 100% rename from src/convert/mkcd_to_website/__init__.py rename to src/mkcd2app/build_project/inputs/__init__.py diff --git a/src/mkcd2app/build_project/inputs/code.py b/src/mkcd2app/build_project/inputs/code.py new file mode 100644 index 0000000..a8e3950 --- /dev/null +++ b/src/mkcd2app/build_project/inputs/code.py @@ -0,0 +1,169 @@ +import logging +import shutil +from io import BytesIO +from pathlib import Path + +import requests +from PIL import Image +from bs4 import BeautifulSoup +from redun import task +from redun.file import ContentDir, ContentFile + +from mkcd2app.config import load_config_from_yaml +from mkcd2app.config.model import GitHubCodeSource, PathAssetSource, PathCodeSource, \ + ShareLinkCodeSource, \ + UrlAssetSource +from mkcd2app.utils.logger import create_logger +from mkcd2app.utils.run import run_cmd + +logger = create_logger(name=__name__, level=logging.INFO) + + +@task(namespace="mkcd2app") +def fetch_code(config_yaml: str, node_modules_for_mkc: ContentDir) -> ContentDir: + """ + Download/clone/copy the source code to the build directory. + + Example input: config_yaml, ./node_modules + Example output: ./racers-source + + :param config_yaml: The raw YAML text of the config file. + :param node_modules_for_mkc: redun.ContentDir that points to the node_modules + directory, this ensures that this task depends on `mkc` being installed. + :return: A redun.ContentDir that points to the source code. + """ + config = load_config_from_yaml(config_yaml) + code_path = Path(config.build_dir) / f"{config.project.path_friendly_name}-source" + logger.info(f"MakeCode Arcade source will be downloaded/copied to {code_path}") + + # Clean previous output, not wasteful because redun already checks our inputs + # and outputs to see if they changed + if code_path.exists(): + shutil.rmtree(code_path) + + # TODO: Implement source code download from GitHub + # TODO: Implement source code copy + match config.inputs.code.root: + case ShareLinkCodeSource(value=url): + logger.debug(f"Downloading source code from {url}") + code_path.mkdir(parents=True) + logger.debug(f"Using `mkc` from {node_modules_for_mkc}") + run_cmd(f"npx mkc download {url}", cwd=code_path) + case GitHubCodeSource(value=url, checkout=checkout_target): + logger.debug(f"Downloading source code from {url}@{checkout_target}") + case PathCodeSource(value=path): + logger.debug(f"Copying source code from {path}") + + logger.debug("Source code downloaded") + return ContentDir(str(code_path)) + + +@task(namespace="mkcd2app") +def build_binary_js(config_yaml: str, code_path: ContentDir) -> ContentFile: + """ + Build the MakeCode Arcade binary.js file + + Example input: config_yaml, ./racers-source + Example output: ./racers-binary-js/binary.js + + :param config_yaml: The raw YAML text of the config file. + :param code_path: A redun.ContentDir where the code is stored. + :return: A redun.ContentFile that points to binary.js + """ + config = load_config_from_yaml(config_yaml) + + logger.info("Building MakeCode Arcade binary.js") + + cwd = Path(code_path.path) + logger.debug(f"Building in cwd {cwd}") + run_cmd("npx mkc build -j", cwd=cwd) + + bin_js_path = cwd / "built" / "binary.js" + logger.debug(f"binary.js available at {bin_js_path}") + + # Copy binary to a stable location outside code_dir so we don't + # pollute fetch_code's ContentDir hash on subsequent runs. + output_path = cwd.parent / f"{config.project.path_friendly_name}-binary-js" / "binary.js" + output_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(bin_js_path, output_path) + shutil.rmtree(cwd / "built") + + return ContentFile(str(output_path)) + + +@task(namespace="mkcd2app") +def download_supporting_files(config_yaml: str) -> ContentDir: + """ + Download all supporting files needed to run binary.js for the website + + Example input: config_yaml + Example output: ./racers-binary-js-support + + :param config_yaml: The raw YAML text of the config file. + :return: redun.ContentDir that points to a directory of files that should be copied + to the website's public folder as is alongside the binary.js file. + """ + config = load_config_from_yaml(config_yaml) + support_path = Path( + config.build_dir) / f"{config.project.path_friendly_name}-binary-js-support" + logger.info(f"Downloading supporting files to {support_path}") + + # Clean previous output, not wasteful because redun handles caching + if support_path.exists(): + shutil.rmtree(support_path) + support_path.mkdir(parents=True) + + logger.debug(f"Downloading main simulator file") + res = requests.get("https://trg-arcade.userpxt.io/---simulator") + res.raise_for_status() + sim_html = res.text + + logger.debug(f"Analyzing sim HTML ({len(sim_html)} chars) for required CSS and JS " + f"files") + soup = BeautifulSoup(sim_html, features="html.parser") + css_links = soup.find_all("link", rel="stylesheet") + js_scripts = soup.find_all("script") + logger.debug(f"Found {len(css_links)} CSS links and {len(js_scripts)} JS scripts") + for css in css_links: + url = css.get("href") + if url: + logger.debug(f"Downloading CSS file {url}") + res = requests.get(url) + res.raise_for_status() + file_name = url.split("/")[-1] + path = support_path / file_name + path.write_text(res.text) + css["href"] = f"./{file_name}" + logger.debug(f"Wrote to {path}") + for js in js_scripts: + url = js.get("src") + if url: + logger.debug(f"Downloading JS file {url}") + res = requests.get(url) + res.raise_for_status() + file_name = url.split("/")[-1] + path = support_path / file_name + path.write_text(res.text) + js["src"] = f"./{file_name}" + logger.debug(f"Wrote to {path}") + new_sim_html = soup.prettify(formatter="html5") + path = support_path / "---simulator.html" + path.write_text(new_sim_html) + logger.debug(f"Wrote modified simulator HTML to {path}") + + if config.inputs.assets.icon: + match config.inputs.assets.icon.root: + case UrlAssetSource(value=url): + logger.debug(f"Downloading icon from {url}") + res = requests.get(str(url)) + res.raise_for_status() + buffer = BytesIO(res.content) + im = Image.open(buffer) + case PathAssetSource(value=path): + logger.debug(f"Opening icon from {path}") + im = Image.open(path) + favicon_path = support_path / "favicon.ico" + logger.debug(f"Saving favicon to {favicon_path}") + im.save(favicon_path) + + return ContentDir(str(support_path)) diff --git a/src/mkcd2app/build_project/website.py b/src/mkcd2app/build_project/website.py new file mode 100644 index 0000000..cf84029 --- /dev/null +++ b/src/mkcd2app/build_project/website.py @@ -0,0 +1,119 @@ +import logging +import shutil +from pathlib import Path + +from redun import task +from redun.file import ContentDir, ContentFile + +from mkcd2app.config import load_config_from_yaml +from mkcd2app.utils.logger import create_logger +from mkcd2app.utils.resources import get_template_path +from mkcd2app.utils.run import run_cmd + +logger = create_logger(name=__name__, level=logging.INFO) + + +def _website_dir(build_dir: Path, name: str, suffix: str) -> Path: + """Return a directory path like ``build_dir/{name}-website{suffix}``.""" + return build_dir / f"{name}-website{suffix}" + + +@task(namespace="mkcd2app") +def copy_website_template(config_yaml: str) -> ContentDir: + """ + Copy the template to the build directory. + + Example input: config_yaml + Example output: racers-website + + :param config_yaml: The raw YAML text of the config file. + :return: A redun.ContentDir that points to the Vite website template directory. + """ + config = load_config_from_yaml(config_yaml) + dst = _website_dir(Path(config.build_dir), config.project.path_friendly_name, "") + logger.info(f"Website will be copied to {dst}") + if dst.exists(): + shutil.rmtree(dst) + with get_template_path("vite-project") as src: + logger.debug(f"Copying template from {src}") + shutil.copytree(src, dst) + logger.debug("Website template copied") + return ContentDir(str(dst)) + + +@task(namespace="mkcd2app") +def fill_website_template(config_yaml: str, + website_path: ContentDir, + bin_js_path: ContentFile, + support_path: ContentDir) -> ContentDir: + """ + "Fill" the website template — copy binary.js and support files into ``public/`` + and update the ````. + + We copy the deps-installed website first so we never mutate a ContentDir + that another task owns. + + Example intput: racers-website + Example output: racers-website-filled + + :param config_yaml: The raw YAML text of the config file. + :param website_path: redun.ContentDir pointing to the deps-installed website. + :param bin_js_path: redun.ContentFile pointing to the binary JS file. + :param support_path: redun.ContentDir pointing to the support file directory. + :return: A redun.ContentDir pointing to the filled website directory. + """ + logger.info("Filling website template") + + config = load_config_from_yaml(config_yaml) + src = Path(website_path.path) + dst = src.parent / f"{src.name}-filled" + if dst.exists(): + shutil.rmtree(dst) + shutil.copytree(src, dst) + + public_path = dst / "public" + logger.debug(f"public directory is at {public_path}") + + logger.debug(f"Copying binary.js from {bin_js_path.path}") + shutil.copy(bin_js_path.path, public_path) + + logger.debug(f"Copying support files from {support_path.path}") + shutil.copytree(support_path.path, public_path, dirs_exist_ok=True) + + title = config.project.title.format(NAME=config.project.name, + VERSION=config.project.version, + AUTHOR=config.project.author) + logger.debug(f"Using title \"{title}\" in index.html") + index_html_path = dst / "index.html" + index_html_text = index_html_path.read_text() + index_html_text = index_html_text.replace("<title>vite-project", + f"{title}") + index_html_path.write_text(index_html_text) + + logger.debug("Website template filled") + return ContentDir(str(dst)) + + +@task(namespace="mkcd2app") +def install_website_dependencies(website_filled_path: ContentDir) -> ContentDir: + """ + Make a copy of the template website, then ``npm ci`` in it. + + We copy so that we never mutate a ContentDir that another task owns, + keeping ContentDir hashes stable for caching. + + Example input: racers-website-filled + Example output: racers-website-filled-deps + + :param website_filled_path: A redun.ContentDir pointing to the template website. + :return: A redun.ContentDir pointing to the deps-installed website copy. + """ + src = Path(website_filled_path.path) + dst = src.parent / f"{src.name}-deps" + logger.info(f"Installing website dependencies — copy {src} -> {dst}") + if dst.exists(): + shutil.rmtree(dst) + shutil.copytree(src, dst) + run_cmd("npm ci", cwd=dst) + logger.debug("Website dependencies installed") + return ContentDir(str(dst)) diff --git a/src/mkcd2app/config/__init__.py b/src/mkcd2app/config/__init__.py new file mode 100644 index 0000000..8f1d4c4 --- /dev/null +++ b/src/mkcd2app/config/__init__.py @@ -0,0 +1,27 @@ +import logging + +import yaml + +from mkcd2app.config.model import BuildConfig +from mkcd2app.utils.logger import create_logger + +logger = create_logger(name=__name__, level=logging.INFO) + + +def load_config_from_yaml(config_text: str) -> BuildConfig: + """ + Load a config from YAML text. + + :param config_text: The text of the config file, not the path. + :return: A Config object. + """ + logger.debug(f"Loading config from {len(config_text)} characters of YAML text") + data = yaml.safe_load(config_text) + + if data["version"] == 1: # only one config version so far + config = BuildConfig(**data) + else: + raise NotImplementedError(f"Unknown config version {data.version}") + logger.debug(f"Loaded config version {config.version}") + + return config diff --git a/src/mkcd2app/config/model.py b/src/mkcd2app/config/model.py new file mode 100644 index 0000000..3d64850 --- /dev/null +++ b/src/mkcd2app/config/model.py @@ -0,0 +1,113 @@ +from typing import Annotated, List, Literal, Optional, Union + +from pydantic import BaseModel, Field, HttpUrl, RootModel + + +# Source code can be a share link, GitHub repo, or a path on disk + +class ShareLinkCodeSource(BaseModel): + type: Literal["share_link"] + value: HttpUrl + + +class GitHubCodeSource(BaseModel): + type: Literal["github"] + value: HttpUrl + checkout: str = "master" + + +class PathCodeSource(BaseModel): + type: Literal["path"] + value: str + + +CodeSource = RootModel[ + Annotated[ + Union[ShareLinkCodeSource, GitHubCodeSource, PathCodeSource], + Field(discriminator="type") + ] +] + + +# Assets can be a URL or path on disk + +class UrlAssetSource(BaseModel): + type: Literal["url"] + value: HttpUrl + + +class PathAssetSource(BaseModel): + type: Literal["path"] + value: str + + +AssetSource = RootModel[ + Annotated[ + Union[UrlAssetSource, PathAssetSource], + Field(discriminator="type") + ] +] + + +# Right now we only have one asset so far, just the icon +class Assets(BaseModel): + icon: AssetSource + + +# To build a project, we need multiple inputs +class Inputs(BaseModel): + code: CodeSource + assets: Assets + + +# Project metadata +class Project(BaseModel): + name: str + path_friendly_name: str + description: Optional[str] = None + author: str + version: str + title: str = "{NAME} v{VERSION}" + + +class WindowConfig(BaseModel): + width: int = 640 + height: int = 480 + + +# Different types of outputs available that we can build + + +class StaticOutput(BaseModel): + type: Literal["static"] + + +class ElectronOutput(BaseModel): + type: Literal["electron"] + window: Optional[WindowConfig] = None + + +class TauriOutput(BaseModel): + type: Literal["tauri"] + identifier: str + window: Optional[WindowConfig] = None + + +OutputOption = RootModel[ + Annotated[ + Union[StaticOutput, ElectronOutput, TauriOutput], + Field(discriminator="type") + ] +] + + +# The entire config +class BuildConfig(BaseModel): + version: int # currently only 1 version so far + project: Project + inputs: Inputs + build_dir: str = Field(..., alias="build_dir") + outputs: List[OutputOption] + + class Config: + populate_by_name = True diff --git a/src/mkcd2app/js_tools/package-lock.json b/src/mkcd2app/js_tools/package-lock.json new file mode 100644 index 0000000..e36cfd3 --- /dev/null +++ b/src/mkcd2app/js_tools/package-lock.json @@ -0,0 +1,209 @@ +{ + "name": "MakeCode-Arcade-to-App", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "makecode": "^1.3.5" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.16", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/commander": { + "version": "8.3.0", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "license": "ISC" + }, + "node_modules/glob": { + "version": "7.2.3", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/makecode": { + "version": "1.3.5", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "commander": "^8.2.0", + "glob": "^7.2.0", + "makecode-core": "^1.7.3", + "node-watch": "^0.7.2", + "semver": "^7.3.7" + }, + "bin": { + "makecode": "makecode", + "mkc": "makecode" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/makecode-core": { + "version": "1.7.9", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.8", + "chalk": "^4.1.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/node-watch": { + "version": "0.7.4", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + } + } +} diff --git a/src/mkcd2app/js_tools/package.json b/src/mkcd2app/js_tools/package.json new file mode 100644 index 0000000..3f36666 --- /dev/null +++ b/src/mkcd2app/js_tools/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "makecode": "^1.3.5" + } +} diff --git a/src/mkcd2app/main.py b/src/mkcd2app/main.py new file mode 100644 index 0000000..e382777 --- /dev/null +++ b/src/mkcd2app/main.py @@ -0,0 +1,92 @@ +import logging +from argparse import ArgumentParser +from pathlib import Path + +import redun +import redun.file +from redun import Scheduler + +# Monkey patch ContentDir to use content-based hashing instead of mtime-based hashing. +# This addresses the issue where cache busts even on identical runs. +# Note: ContentDir.__iter__() already yields ContentFile objects (via +# ContentFileClasses), so f.hash is content-based — no need to create redundant +# ContentFile instances. +def _content_dir_calc_hash(self, files=None): + if files is None: + files = list(self) + from redun.hashing import hash_struct + return hash_struct([self.type_basename, self.path] + sorted(f.hash for f in files)) + +redun.file.ContentDir._calc_hash = _content_dir_calc_hash + +# Also patch Dir._calc_hash to prevent mtime-based hashing leak through any +# code path that uses Dir directly (e.g. FileSystem.iter_file_hashes). +# Note: we intentionally drop the `files` param because Dir.update_hash() +# passes File objects (mtime-based), not ContentFile objects. Re-iterating +# via ContentDir gives properly content-based hashing. +def _dir_calc_hash(self, files=None): + from redun.file import ContentDir + return ContentDir(self.path)._calc_hash() + +redun.file.Dir._calc_hash = _dir_calc_hash + +from mkcd2app.build_project import build_project +from mkcd2app.config import load_config_from_yaml +from mkcd2app.utils.logger import create_logger, set_all_stdout_logger_levels + +logger = create_logger(name=__name__, level=logging.INFO) + + +def main(): + parser = ArgumentParser(description="Convert your MakeCode Arcade games into a " + "standalone offline executable!") + parser.add_argument("--debug", action="store_true", + help="Enable debug logging. This must go first before the sub " + "command.") + subparsers = parser.add_subparsers(required=True, dest="command") + # build subcommand + parser_build = subparsers.add_parser("build", + help="Build your MakeCode Arcade game.") + parser_build.add_argument("config", type=str, help="Path to the YAML config file.") + + args = parser.parse_args() + debug = bool(args.debug) + if debug: + set_all_stdout_logger_levels(logging.DEBUG) + logger.debug(f"Received arguments: {args}") + + if args.command == "build": + logger.debug("Building project") + + config_path = Path(args.config) + logger.debug(f"Loading config from {config_path}") + config_text = config_path.read_text() + + # Parse once only to extract build_dir for the redun DB path. + # The raw YAML text is passed to redun tasks so that argument + # hashing is deterministic (string) rather than pickle-based + # (which is non-deterministic due to pydantic's set fields). + config = load_config_from_yaml(config_text) + build_dir = Path(config.build_dir) + build_dir.mkdir(parents=True, exist_ok=True) + db_uri = f"sqlite:///{build_dir.resolve() / '.redun-cache.db'}" + logger.debug(f"redun cache DB: {db_uri}") + # noinspection PyUnresolvedReferences + redun_config = redun.config.Config({ + "scheduler": {"log_level": "DEBUG"}, + "backend": {"db_uri": db_uri}, + }) + scheduler = Scheduler(config=redun_config) + # Load/migrate the backend so the persistent DB is properly set up. + # Without this, providing a custom db_uri skips the automatic + # engine creation and migration that the in-memory default does. + scheduler.load() + result = scheduler.run( + build_project(config_text), + ) + + logger.debug(f"Build finished, result is at {result}") + + +if __name__ == "__main__": + main() diff --git a/src/templates/tauri_files/.gitignore b/src/mkcd2app/templates/vite-project/.gitignore similarity index 100% rename from src/templates/tauri_files/.gitignore rename to src/mkcd2app/templates/vite-project/.gitignore diff --git a/src/templates/website_files/.prettierignore b/src/mkcd2app/templates/vite-project/.prettierignore similarity index 100% rename from src/templates/website_files/.prettierignore rename to src/mkcd2app/templates/vite-project/.prettierignore diff --git a/src/mkcd2app/templates/vite-project/README.md b/src/mkcd2app/templates/vite-project/README.md new file mode 100644 index 0000000..08412ab --- /dev/null +++ b/src/mkcd2app/templates/vite-project/README.md @@ -0,0 +1,75 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is enabled on this template. See [this documentation](https://react.dev/learn/react-compiler) for more information. + +Note: This will impact Vite dev & build performances. + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(["dist"]), + { + files: ["**/*.{ts,tsx}"], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ["./tsconfig.node.json", "./tsconfig.app.json"], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]); +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from "eslint-plugin-react-x"; +import reactDom from "eslint-plugin-react-dom"; + +export default defineConfig([ + globalIgnores(["dist"]), + { + files: ["**/*.{ts,tsx}"], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs["recommended-typescript"], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ["./tsconfig.node.json", "./tsconfig.app.json"], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]); +``` diff --git a/src/mkcd2app/templates/vite-project/eslint.config.js b/src/mkcd2app/templates/vite-project/eslint.config.js new file mode 100644 index 0000000..da6f026 --- /dev/null +++ b/src/mkcd2app/templates/vite-project/eslint.config.js @@ -0,0 +1,22 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; +import { defineConfig, globalIgnores } from "eslint/config"; + +export default defineConfig([ + globalIgnores(["dist"]), + { + files: ["**/*.{ts,tsx}"], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + globals: globals.browser, + }, + }, +]); diff --git a/src/templates/website_files/index.html b/src/mkcd2app/templates/vite-project/index.html similarity index 75% rename from src/templates/website_files/index.html rename to src/mkcd2app/templates/vite-project/index.html index 31a5cf5..a9a42a2 100644 --- a/src/templates/website_files/index.html +++ b/src/mkcd2app/templates/vite-project/index.html @@ -2,12 +2,12 @@ - - {TITLE} + + vite-project
- + diff --git a/src/mkcd2app/templates/vite-project/package-lock.json b/src/mkcd2app/templates/vite-project/package-lock.json new file mode 100644 index 0000000..6eacf1a --- /dev/null +++ b/src/mkcd2app/templates/vite-project/package-lock.json @@ -0,0 +1,3261 @@ +{ + "name": "vite-project", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vite-project", + "version": "0.0.0", + "dependencies": { + "prettier": "^3.9.5", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-toastify": "^11.1.0" + }, + "devDependencies": { + "@babel/core": "^7.29.7", + "@eslint/js": "^10.0.1", + "@rolldown/plugin-babel": "^0.2.3", + "@types/babel__core": "^7.20.5", + "@types/node": "^24.13.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "babel-plugin-react-compiler": "^1.0.0", + "eslint": "^10.6.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.3", + "globals": "^17.7.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.62.0", + "vite": "^8.1.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.140.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.140.0.tgz", + "integrity": "sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.0.tgz", + "integrity": "sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.0.tgz", + "integrity": "sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.0.tgz", + "integrity": "sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.0.tgz", + "integrity": "sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.0.tgz", + "integrity": "sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.0.tgz", + "integrity": "sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.0.tgz", + "integrity": "sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.0.tgz", + "integrity": "sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.0.tgz", + "integrity": "sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.0.tgz", + "integrity": "sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.0.tgz", + "integrity": "sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.0.tgz", + "integrity": "sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/core": "1.11.2", + "@emnapi/runtime": "1.11.2", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.0.tgz", + "integrity": "sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.0.tgz", + "integrity": "sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/plugin-babel": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/plugin-babel/-/plugin-babel-0.2.3.tgz", + "integrity": "sha512-+zEk16yGlz1F9STiRr6uG9hmIXb6nprjLczV/htGptYuLoCuxb+itZ03RKCEeOhBpDDd1NU7qF6x1VLMUp62bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=22.12.0 || ^24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.29.0 || ^8.0.0-rc.1", + "@babel/plugin-transform-runtime": "^7.29.0 || ^8.0.0-rc.1", + "@babel/runtime": "^7.27.0 || ^8.0.0-rc.1", + "rolldown": "^1.0.0-rc.5", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@babel/plugin-transform-runtime": { + "optional": true + }, + "@babel/runtime": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", + "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/type-utils": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.64.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz", + "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", + "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", + "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", + "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.64.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", + "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/babel-plugin-react-compiler": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz", + "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.44", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.44.tgz", + "integrity": "sha512-T3ghW+sl/ZJ8w1v/yQx3qvJ9040DWoLBz8JT/CILbAKcFyG9b2MRe75v6W5uXjv6uH1lumK2Kv46y2zSkcej0Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz", + "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001803", + "electron-to-chromium": "^1.5.389", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.393", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.393.tgz", + "integrity": "sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz", + "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.3.tgz", + "integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", + "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.20", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.20.tgz", + "integrity": "sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz", + "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "node_modules/react-toastify": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.1.0.tgz", + "integrity": "sha512-e9h23x3phN0wbFeB6yovmWp7lobzV4CaCH0LO8nVP6H7Y+3GbcLpIzMm9dJhcp1RXbpyfvjgpfXqO80QAmn7sg==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + } + }, + "node_modules/rolldown": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.0.tgz", + "integrity": "sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@oxc-project/types": "=0.140.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.0", + "@rolldown/binding-darwin-arm64": "1.2.0", + "@rolldown/binding-darwin-x64": "1.2.0", + "@rolldown/binding-freebsd-x64": "1.2.0", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.0", + "@rolldown/binding-linux-arm64-gnu": "1.2.0", + "@rolldown/binding-linux-arm64-musl": "1.2.0", + "@rolldown/binding-linux-ppc64-gnu": "1.2.0", + "@rolldown/binding-linux-s390x-gnu": "1.2.0", + "@rolldown/binding-linux-x64-gnu": "1.2.0", + "@rolldown/binding-linux-x64-musl": "1.2.0", + "@rolldown/binding-openharmony-arm64": "1.2.0", + "@rolldown/binding-wasm32-wasi": "1.2.0", + "@rolldown/binding-win32-arm64-msvc": "1.2.0", + "@rolldown/binding-win32-x64-msvc": "1.2.0" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz", + "integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.64.0", + "@typescript-eslint/parser": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/vite/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/vite/node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/src/mkcd2app/templates/vite-project/package.json b/src/mkcd2app/templates/vite-project/package.json new file mode 100644 index 0000000..8cab403 --- /dev/null +++ b/src/mkcd2app/templates/vite-project/package.json @@ -0,0 +1,39 @@ +{ + "name": "vite-project", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "lint": "eslint ", + "writeLint": "eslint --fix ", + "format": "prettier --check ", + "writeFormat": "prettier --write ", + "preview": "vite preview", + "build": "tsc -b && vite build" + }, + "dependencies": { + "prettier": "^3.9.5", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-toastify": "^11.1.0" + }, + "devDependencies": { + "@babel/core": "^7.29.7", + "@eslint/js": "^10.0.1", + "@rolldown/plugin-babel": "^0.2.3", + "@types/babel__core": "^7.20.5", + "@types/node": "^24.13.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "babel-plugin-react-compiler": "^1.0.0", + "eslint": "^10.6.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.3", + "globals": "^17.7.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.62.0", + "vite": "^8.1.1" + } +} diff --git a/src/templates/tauri_files/src-tauri/icons/.keep b/src/mkcd2app/templates/vite-project/public/.keep similarity index 100% rename from src/templates/tauri_files/src-tauri/icons/.keep rename to src/mkcd2app/templates/vite-project/public/.keep diff --git a/src/templates/website_files/src/App.css b/src/mkcd2app/templates/vite-project/src/App.css similarity index 93% rename from src/templates/website_files/src/App.css rename to src/mkcd2app/templates/vite-project/src/App.css index 6d6748a..6783cd2 100644 --- a/src/templates/website_files/src/App.css +++ b/src/mkcd2app/templates/vite-project/src/App.css @@ -1,7 +1,7 @@ body { background: black; overflow: hidden; - margin: 0 + margin: 0; } iframe { diff --git a/src/templates/website_files/src/App.tsx b/src/mkcd2app/templates/vite-project/src/App.tsx similarity index 82% rename from src/templates/website_files/src/App.tsx rename to src/mkcd2app/templates/vite-project/src/App.tsx index 486b744..5a77c84 100644 --- a/src/templates/website_files/src/App.tsx +++ b/src/mkcd2app/templates/vite-project/src/App.tsx @@ -5,9 +5,9 @@ import { loadingToast, type LoadingToastCallbacks, } from "./utils/toasts.ts"; -import {toast} from "react-toastify"; -import {GameConfiguration} from "./gameConfiguration.ts"; -import {positionFixedElement} from "./utils/position.ts"; +import { toast } from "react-toastify"; +import { GameConfiguration } from "./gameConfiguration.ts"; +import { positionFixedElement } from "./utils/position.ts"; function App(): React.ReactNode { const simulatorRef = React.useRef(null); @@ -21,8 +21,7 @@ function App(): React.ReactNode { const restartGameToastCallbacksRef = React.useRef( createEmptyLoadingToastCallbacks(), ); - const gameCrashToastCloseCallbackRef = React.useRef<() => void>(() => { - }); + const gameCrashToastCloseCallbackRef = React.useRef<() => void>(() => {}); const [showNoFocusMessage, setShowNoFocusMessage] = React.useState(false); const [statsInnerText, setStatsInnerText] = React.useState(""); @@ -31,7 +30,6 @@ function App(): React.ReactNode { setSimState(JSON.parse(localStorage.getItem("simState") ?? "{}")); } catch (err) { console.warn( - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `Failed to load sim state, maybe first time run or simState is empty?\n${err}`, ); setSimState({}); @@ -46,10 +44,10 @@ function App(): React.ReactNode { loadingGameToastCallbacksRef.current = GameConfiguration.Toasts .ENABLE_LOADING_GAME_TOAST ? loadingToast( - GameConfiguration.Toasts.LOADING_GAME_TOAST_PENDING_MSG, - GameConfiguration.Toasts.LOADING_GAME_TOAST_SUCCESS_MSG, - GameConfiguration.Toasts.LOADING_GAME_TOAST_ERROR_MSG, - ) + GameConfiguration.Toasts.LOADING_GAME_TOAST_PENDING_MSG, + GameConfiguration.Toasts.LOADING_GAME_TOAST_SUCCESS_MSG, + GameConfiguration.Toasts.LOADING_GAME_TOAST_ERROR_MSG, + ) : createEmptyLoadingToastCallbacks(); fetch("binary.js") .then((res) => { @@ -103,7 +101,7 @@ function App(): React.ReactNode { function stopSim() { console.log("Stopping simulator"); - simulatorRef.current?.contentWindow?.postMessage({type: "stop"}); + simulatorRef.current?.contentWindow?.postMessage({ type: "stop" }); } /* eslint-disable */ @@ -124,10 +122,10 @@ function App(): React.ReactNode { restartGameToastCallbacksRef.current = GameConfiguration.Toasts .ENABLE_RESTARTING_GAME_TOAST ? loadingToast( - GameConfiguration.Toasts.RESTARTING_GAME_TOAST_PENDING_MSG, - GameConfiguration.Toasts.RESTARTING_GAME_TOAST_SUCCESS_MSG, - GameConfiguration.Toasts.RESTARTING_GAME_TOAST_ERROR_MSG, - ) + GameConfiguration.Toasts.RESTARTING_GAME_TOAST_PENDING_MSG, + GameConfiguration.Toasts.RESTARTING_GAME_TOAST_SUCCESS_MSG, + GameConfiguration.Toasts.RESTARTING_GAME_TOAST_ERROR_MSG, + ) : createEmptyLoadingToastCallbacks(); stopSim(); gameCrashToastCloseCallbackRef.current(); @@ -162,7 +160,7 @@ function App(): React.ReactNode { console.error(data); if (GameConfiguration.Toasts.ENABLE_POSSIBLE_GAME_CRASH_TOAST) { toast.error( - ({closeToast}) => { + ({ closeToast }) => { gameCrashToastCloseCallbackRef.current = closeToast; return (
@@ -178,13 +176,13 @@ function App(): React.ReactNode { restartGameToastCallbacksRef.current = GameConfiguration .Toasts.ENABLE_RESTARTING_GAME_TOAST ? loadingToast( - GameConfiguration.Toasts - .RESTARTING_GAME_TOAST_PENDING_MSG, - GameConfiguration.Toasts - .RESTARTING_GAME_TOAST_SUCCESS_MSG, - GameConfiguration.Toasts - .RESTARTING_GAME_TOAST_ERROR_MSG, - ) + GameConfiguration.Toasts + .RESTARTING_GAME_TOAST_PENDING_MSG, + GameConfiguration.Toasts + .RESTARTING_GAME_TOAST_SUCCESS_MSG, + GameConfiguration.Toasts + .RESTARTING_GAME_TOAST_ERROR_MSG, + ) : createEmptyLoadingToastCallbacks(); stopSim(); setTimeout(() => { @@ -204,10 +202,10 @@ function App(): React.ReactNode { }, { autoClose: - GameConfiguration.Toasts.POSSIBLE_GAME_CRASH_TOAST_AUTOCLOSE, + GameConfiguration.Toasts.POSSIBLE_GAME_CRASH_TOAST_AUTOCLOSE, closeOnClick: - GameConfiguration.Toasts - .POSSIBLE_GAME_CRASH_TOAST_CLOSE_ON_CLICK, + GameConfiguration.Toasts + .POSSIBLE_GAME_CRASH_TOAST_CLOSE_ON_CLICK, }, ); } @@ -233,7 +231,7 @@ function App(): React.ReactNode { statsRef.current, GameConfiguration.DebugStats.STATS_LOCATION, ); - setStatsInnerText(statsText ?? "") + setStatsInnerText(statsText ?? ""); statsRef.current.innerText = statsText ?? ""; } }, 100); @@ -251,7 +249,7 @@ function App(): React.ReactNode { const checkFocusID = setInterval(() => { setShowNoFocusMessage( !document.hasFocus() || - !simulatorRef.current?.contentDocument?.hasFocus(), + !simulatorRef.current?.contentDocument?.hasFocus(), ); }, 100); @@ -265,7 +263,6 @@ function App(): React.ReactNode {