commit 4e83f159f0f8ae031ac255d59ea9100c592026a5 Author: kejiz Date: Thu Sep 18 10:19:08 2025 +0800 Initial commit: Add maimaiDX API web application with AimeDB scanning and logging features diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a7e12d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Dependencies +node_modules/ +__pycache__/ +*.pyc +.env + +# IDE +.vscode/ +.idea/ + +# Logs +logs/ +*.log + +# Build outputs +dist/ +build/ +*.exe +*.dmg +*.app + +# OS +.DS_Store +Thumbs.db + +# Python +*.pyo +*.pyd +*.pyc +*.pyo +.pytest_cache/ +.coverage +htmlcov/ + +# Virtual environments +.venv/ +venv/ +env/ + +# Frontend build outputs +frontend/dist/ +frontend/node_modules/ + +# Backend build outputs +backend/__pycache__/ +backend/*.pyc \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7e5c97d --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Maimai DX Web 工具 + +这是一个基于 Python (FastAPI) 和 Vue.js (Element Plus) 构建的 Web 工具,旨在提供一个用户友好的界面来管理和查看 maimai DX 模拟器的数据。 + +## 项目结构 + +``` +maimai-web-app/ +├── backend/ # Python 后端服务 (FastAPI) +└── frontend/ # Vue.js 前端应用 (Element Plus) +``` + +## 运行项目 + +请确保您的系统已安装 Python 3.8+ 和 Node.js (包含 npm)。 + +### 1\. 启动后端服务 + +打开您的命令行工具,进入 `maimai-web-app/backend` 目录,然后安装 Python 依赖并启动服务: + +```bash +cd maimai-web-app/backend +pip install -r requirements.txt +python main.py +``` + +**注意:** + +* 后端服务默认运行在 `http://0.0.0.0:8000`。如果您需要从其他设备访问(例如手机),请确保您的防火墙允许 8000 端口的传入连接,并且您的设备在同一局域网内,或者您已正确配置了端口映射。 +* `main.py` 中的 `testUid8` 是一个默认的用户ID,您可以在 `MyConfig.py` 中修改它。 + +### 2\. 启动前端应用 + +打开一个新的命令行工具(不要关闭后端服务的窗口),进入 `maimai-web-app/frontend` 目录,然后安装 Node.js 依赖并启动应用: + +```bash +cd maimai-web-app/frontend +npm install +npm run dev -- --port 5174 +``` + +前端应用默认运行在 `http://localhost:5174`。如果该端口被占用,它会自动尝试其他端口。您也可以通过 `--port` 参数指定端口,例如 `npm run dev -- --port 3000`。 + +### 3\. 访问应用 + +在浏览器中打开前端应用提供的地址(通常是 `http://localhost:5174`)。 + +## 已实现功能 + +### 乐曲列表 + +* 浏览所有 maimai DX 乐曲。 +* 支持按歌曲名和艺术家搜索。 + +### 用户中心 + +* **用户选择**: 通过输入用户ID获取玩家的完整信息或预览信息。 +* **玩家操作**: + + * **领取登录奖励**: 领取所有可用的每日登录奖励。 + * **道具管理**: 解锁指定种类和ID的道具。 + * **票券管理**: 购买指定类型的票券。 + * **分数管理**: 上传或删除指定乐曲的分数记录。 + * **上传至水鱼**: 将用户的分数上传至 [diving-fish.com](https://www.diving-fish.com/maimaidx/prober) 进行详细分析。 + * **通用接口调用**: 通过输入API名称,调用任意 `GetUser...Api` 接口,并显示原始响应。 + * **危险操作**: 清空所有票券、强制用户登出。 + +* **分数详情**: 获取并显示用户的所有乐曲分数记录。 + +### 更新链接 + +* 获取 AuthLiteDelivery 更新链接列表。 +* 解析每个更新链接指向的 INI 文件,并美观地展示其中的更新信息(如游戏描述、发布时间、主更新包和可选更新包链接)。 + +## 贡献 + +欢迎提交 Pull Request 或报告 Bug。在提交代码之前,请确保您的代码符合项目规范。 + +## 许可证 + +本项目采用 MIT 许可证。详情请参阅 `LICENSE` 文件。 + diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..178b014 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,179 @@ +# Anti-leak +Private_Static_Settings.py +MyConfig.py + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# PyPI configuration file +.pypirc +Private_Static_Settings.py +Private_Static_Settings.py + +/.python-version diff --git a/backend/APILogger.py b/backend/APILogger.py new file mode 100644 index 0000000..e1028b9 --- /dev/null +++ b/backend/APILogger.py @@ -0,0 +1,84 @@ +import os +import json +from datetime import datetime +from loguru import logger + + +class APILogger: + def __init__(self, log_dir="logs"): + self.log_dir = log_dir + self._setup_logging() + + def _setup_logging(self): + """设置日志配置""" + # 创建日志目录(如果不存在) + if not os.path.exists(self.log_dir): + os.makedirs(self.log_dir) + + # 配置loguru + logger.add( + f"{self.log_dir}/app_{{time}}.log", + rotation="100 MB", + retention="30 days", + format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}" + ) + logger.add( + f"{self.log_dir}/error_{{time}}.log", + level="ERROR", + rotation="100 MB", + retention="90 days", + format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}" + ) + + def log_request(self, method, url, headers, body=None): + """记录API请求""" + log_data = { + "type": "request", + "method": method, + "url": str(url), + "headers": dict(headers), + "timestamp": datetime.now().isoformat() + } + + if body: + log_data["body"] = body + + logger.info(f"API Request: {json.dumps(log_data, ensure_ascii=False)}") + + def log_response(self, status_code, response_data, process_time=None): + """记录API响应""" + log_data = { + "type": "response", + "status_code": status_code, + "response_data": response_data, + "process_time": process_time, + "timestamp": datetime.now().isoformat() + } + + if status_code >= 400: + logger.error(f"API Response: {json.dumps(log_data, ensure_ascii=False)}") + else: + logger.info(f"API Response: {json.dumps(log_data, ensure_ascii=False)}") + + def log_error(self, error_msg, error_details=None): + """记录错误""" + log_data = { + "type": "error", + "error_msg": error_msg, + "error_details": error_details, + "timestamp": datetime.now().isoformat() + } + + logger.error(f"API Error: {json.dumps(log_data, ensure_ascii=False)}") + + def log_info(self, message): + """记录信息""" + logger.info(message) + + def log_warning(self, message): + """记录警告""" + logger.warning(message) + + +# 创建全局日志实例 +api_logger = APILogger() \ No newline at end of file diff --git a/backend/API_AimeDB.py b/backend/API_AimeDB.py new file mode 100644 index 0000000..7f93952 --- /dev/null +++ b/backend/API_AimeDB.py @@ -0,0 +1,132 @@ +# 100% Standalone 的舞萌国服 AimeDB 通讯实现 +# Ver.CN1.41 +# Mainline 20250203 + +import hashlib +import time +import requests +import json +import re +from loguru import logger + +# 常量 +CHIP_ID = "A63E-01E68606624" +COMMON_KEY = "XcW5FW4cPArBXEk4vzKz3CIrMuA5EVVW" +API_URL = "http://ai.sys-allnet.cn/wc_aime/api/get_data" + + +# 计算 SHA256 +def getSHA256(input_str): + """SHA256计算""" + return hashlib.sha256(input_str.encode("utf-8")).hexdigest().upper() + + +# 生成时间戳 +def generateSEGATimestamp(): + """SEGA格式的 YYMMDDHHMMSS 时间戳(sb玩意)""" + return time.strftime("%y%m%d%H%M%S", time.localtime()) + + +# 计算认证 key +def calcSEGAAimeDBAuthKey( + varString: str, timestamp: str, commonKey: str = "XcW5FW4cPArBXEk4vzKz3CIrMuA5EVVW" +) -> str: + """计算 SEGA AimeDB 的认证 key""" + return ( + hashlib.sha256((varString + timestamp + commonKey).encode("utf-8")) + .hexdigest() + .upper() + ) + + +def apiAimeDB(qrCode): + """AimeDB 扫码 API 实现""" + # 生成一个时间戳 + timestamp = generateSEGATimestamp() + + # 使用时间戳计算 key + currentKey = calcSEGAAimeDBAuthKey(CHIP_ID, timestamp, COMMON_KEY) + + # 构造请求数据 + payload = { + "chipID": CHIP_ID, + "openGameID": "MAID", + "key": currentKey, + "qrCode": qrCode, + "timestamp": timestamp, + } + + # 输出准备好的请求数据 + print("Payload:", json.dumps(payload, separators=(",", ":"))) + + # 发送 POST 请求 + headers = { + "Connection": "Keep-Alive", + "Host": API_URL.split("//")[-1].split("/")[0], + "User-Agent": "WC_AIME_LIB", + "Content-Type": "application/json", + } + response = requests.post( + API_URL, data=json.dumps(payload, separators=(",", ":")), headers=headers + ) + + # 返回服务器的响应 + return response + + +def isSGWCFormat(input_string: str) -> bool: + """简单检查二维码字符串是否符合格式""" + if ( + len(input_string) != 84 # 长度 + or not input_string.startswith("SGWCMAID") # 识别字 + or re.match("^[0-9A-F]+$", input_string[20:]) is None # 有效字符 + ): + return False + else: + return True + + +def implAimeDB(qrCode: str, isAlreadyFinal: bool = False) -> str: + """ + Aime DB 的请求的参考实现。 + 提供完整 QRCode 内容,返回响应的字符串(Json格式) + """ + if isAlreadyFinal: + qr_code_final = qrCode + else: + # 提取有效部分(Hash) + qr_code_final = qrCode[20:] + + # 发送请求 + response = apiAimeDB(qr_code_final) + + # 获得结果 + print("implAimeDB: StatusCode is ", response.status_code) + print("implAimeDB: Response Body is:", response.text) + return response.text + + +def implGetUID(qr_content: str) -> dict: + """ + 包装后的 UID 扫码器实现。 + 此函数会返回 AimeDB 传回的 Json 转成 Python 字典的结果。 + 主要特点是添加了几个新的错误码(6000x)用来应对程序的错误。 + """ + # 检查格式 + if not isSGWCFormat(qr_content): + return {"errorID": 60001} # 二维码内容明显无效 + + # 发送请求并处理响应 + try: + result = json.loads(implAimeDB(qr_content)) + logger.info(f"QRScan Got Response {result}") + except Exception: + return {"errorID": 60002} # 无法解码 Response 的内容 + + # 返回结果 + return result + + +if __name__ == "__main__": + userInputQR = input("QRCode: ") + print(implAimeDB(userInputQR)) diff --git a/backend/API_AuthLiteDelivery.py b/backend/API_AuthLiteDelivery.py new file mode 100644 index 0000000..4b3cb84 --- /dev/null +++ b/backend/API_AuthLiteDelivery.py @@ -0,0 +1,113 @@ +# All.Net AuthLite 更新获取 + +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +import httpx +from loguru import logger +from urllib.parse import parse_qs +import configparser as ini + +LITE_AUTH_KEY = bytes( + [47, 63, 106, 111, 43, 34, 76, 38, 92, 67, 114, 57, 40, 61, 107, 71] +) +LITE_AUTH_IV = bytes.fromhex("00000000000000000000000000000000") + + +def auth_lite_encrypt(plaintext: str) -> bytes: + # 构造数据:16字节头 + 16字节0前缀 + 明文 + content = bytes(32) + plaintext.encode("utf-8") + data = content + # 填充并加密 + padded_data = pad(data, AES.block_size) + cipher = AES.new(LITE_AUTH_KEY, AES.MODE_CBC, LITE_AUTH_IV) + return cipher.encrypt(padded_data) + + +def auth_lite_decrypt(ciphertext: bytes) -> str: + # 解密并去除填充 + cipher = AES.new(LITE_AUTH_KEY, AES.MODE_CBC, LITE_AUTH_IV) + decrypted_data = unpad(cipher.decrypt(ciphertext), AES.block_size) + # 提取内容并解码 + content = decrypted_data[16:] # 去除头部的16字节 + return content.decode("utf-8").strip() + + +def getRawDelivery(title_ver: str = "1.51"): + encrypted = auth_lite_encrypt( + f"title_id=SDGB&title_ver={title_ver}&client_id=A63E01C2805" + ) + r = httpx.post( + "http://at.sys-allnet.cn/net/delivery/instruction", + data=encrypted, + headers={"User-Agent": "SDGB;Windows/Lite", "Pragma": "DFI"}, + ) + resp_data = r.content + decrypted_str = auth_lite_decrypt(resp_data) + # 过滤所有控制字符 + decrypted_str = "".join([i for i in decrypted_str if 31 < ord(i) < 127]) + logger.info(f"RAW Response: {decrypted_str}") + + return decrypted_str + + +def parseRawDelivery(deliveryStr): + """解析 RAW 的 Delivery 字符串,返回其中的有效的 instruction URL 的列表""" + parsedResponseDict: dict[str, str] = { + key: value[0] for key, value in parse_qs(deliveryStr).items() + } + urlList = parsedResponseDict["uri"].split("|") + # 过滤掉空字符串和内容为 null 的情况 + urlList = [url for url in urlList if url and url != "null"] + logger.info(f"Parsed URL List: {urlList}") + validURLs = [] + for url in urlList: + # 检查是否是 HTTPS 的 URL,以及是否是 txt 文件,否则忽略 + if not url.startswith("https://") or not url.endswith(".txt"): + logger.warning(f"Invalid URL will be ignored: {url}") + continue + validURLs.append(url) + logger.info(f"Verified Valid URLs: {validURLs}") + return validURLs + + +def getUpdateIniFromURL(url): + # 发送请求 + response = httpx.get( + url, headers={"User-Agent": "SDGB;Windows/Lite", "Pragma": "DFI"} + ) + logger.info(f"成功自 {url} 获取更新信息") + return response.text + + +def parseUpdateIni(iniText): + # 解析配置 + config = ini.ConfigParser(allow_no_value=True) + config.read_string(iniText) + logger.info(f"成功解析配置文件,包含的节有:{config.sections()}") + + # 获取 COMMON 节的配置 + common = config["COMMON"] + + game_desc = common.get("GAME_DESC", "").strip('"') + release_time = common.get("RELEASE_TIME", "").replace("T", " ") + main_file = common.get("INSTALL1", "") + + optional_files = [] + if "OPTIONAL" in config: + for key, url in config.items("OPTIONAL"): + optional_files.append(f"{url.split('/')[-1]} {url}") + + return { + "game_desc": game_desc, + "release_time": release_time, + "main_file": main_file, + "optional_files": optional_files, + } + + +if __name__ == "__main__": + raw = getRawDelivery("1.51") + urlList = parseRawDelivery(raw) + for url in urlList: + iniText = getUpdateIniFromURL(url) + message = parseUpdateIni(iniText) diff --git a/backend/API_TitleServer.py b/backend/API_TitleServer.py new file mode 100644 index 0000000..267b268 --- /dev/null +++ b/backend/API_TitleServer.py @@ -0,0 +1,352 @@ +# 舞萌DX +# 标题服务器通讯实现 +import zlib +import hashlib +import random +import time + +import httpx +from loguru import logger +from ctypes import c_int32 +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad + +from Config import ( + useProxy, + proxyUrl, +) + + +AesKey = "a>32bVP7v<63BVLkY[xM>daZ1s9MBP bytes: + cipher = AES.new(self.key, self.mode, self.iv) + content_padded = pad(content, AES.block_size) + encrypted_bytes = cipher.encrypt(content_padded) + return encrypted_bytes + + def decrypt(self, content): + cipher = AES.new(self.key, self.mode, self.iv) + decrypted_padded = cipher.decrypt(content) + decrypted = unpad(decrypted_padded, AES.block_size) + return decrypted + + def pkcs7unpadding(self, text): + length = len(text) + unpadding = ord(text[length - 1]) + return text[0 : length - unpadding] + + def pkcs7padding(self, text): + bs = 16 + length = len(text) + bytes_length = len(text.encode("utf-8")) + padding_size = length if (bytes_length == length) else bytes_length + padding = bs - padding_size % bs + padding_text = chr(padding) * padding + return text + padding_text + + +def getSDGBApiHash(api): + # API 的 Hash 的生成 + # 有空做一下 Hash 的彩虹表? + return hashlib.md5((api + "MaimaiChn" + ObfuscateParam).encode()).hexdigest() + + +def apiSDGB( + data: str, + targetApi: str, + userAgentExtraData: str, + noLog: bool = False, + timeout: int = 5, + maxRetries: int = 3, +) -> str: + """ + 舞萌DX 2025 API 通讯用函数 + + :param data: 请求数据 + :param targetApi: 使用的 API + :param userAgentExtraData: UA 附加信息,机台相关则为狗号(如A63E01E9564),用户相关则为 UID + :param noLog: 是否不记录日志 + :param timeout: 请求超时时间(秒) + :return: 解码后的响应数据 + """ + # 处理参数 + agentExtra = str(userAgentExtraData) + aes = aes_pkcs7(AesKey, AesIV) + endpoint = "https://maimai-gm.wahlap.com:42081/Maimai2Servlet/" + + # 准备好请求数据 + requestDataFinal = aes.encrypt(zlib.compress(data.encode("utf-8"))) + + if not noLog: + logger.debug(f"[Stage 1] 准备开始请求 {targetApi},以 {data}") + + retries = 0 + while retries < maxRetries: + try: + # 配置 HTTP 客户端 + if useProxy and proxyUrl: + logger.debug("使用代理") + httpClient = httpx.Client(proxy=proxyUrl, verify=False) + else: + httpClient = httpx.Client(verify=False) + + api_hash = getSDGBApiHash(targetApi) + + logger.info(f"hash: {api_hash}") + + # 发送请求 + response = httpClient.post( + url=endpoint + api_hash, + headers={ + "User-Agent": f"{api_hash}#{agentExtra}", + "Content-Type": "application/json", + "Mai-Encoding": "1.50", + "Accept-Encoding": "", + "Charset": "UTF-8", + "Content-Encoding": "deflate", + "Expect": "100-continue", + }, + content=requestDataFinal, # 数据 + timeout=timeout, + ) + + if not noLog: + logger.info(f"[Stage 2] {targetApi} 请求结果: {response.status_code}") + + if response.status_code != 200: + errorMessage = f"[Stage 2] 请求失败: {response.status_code}" + logger.error(errorMessage) + raise SDGBRequestError(errorMessage) + + # 处理响应内容 + responseContentRaw = response.content + + # 先尝试解密 + try: + responseContentDecrypted = aes.decrypt(responseContentRaw) + if not noLog: + logger.debug("[Stage 3] Decryption SUCCESS.") + except Exception as e: + logger.warning( + f"[Stage 3] Decryption FAILED. Raw Content: {responseContentRaw}, Error: {e}" + ) + raise SDGBResponseError("Decryption failed") + # 然后尝试解压 + try: + # 看看文件头是否是压缩过的 + if responseContentDecrypted.startswith(b"\x78\x9c"): + logger.debug("[Stage 4] Zlib detected, decompressing...") + responseContentFinal = zlib.decompress( + responseContentDecrypted + ).decode("utf-8") + else: + logger.warning( + f"[Stage 4] Not Zlib Format!! using raw content: {responseContentDecrypted}" + ) + responseContentFinal = responseContentDecrypted.decode("utf-8") + # 完成解压 + if not noLog: + logger.debug( + f"[Stage 4] Process OK, Content: {responseContentFinal}" + ) + # 最终处理,检查是否是 JSON 格式 + if responseContentFinal.startswith( + "{" + ) and responseContentFinal.endswith("}"): + # 如果是 JSON 格式,直接返回 + logger.debug("[Stage 5] Response is JSON, returning.") + return responseContentFinal + else: + # 如果不是 JSON 格式,直接返回但是警告 + logger.warning( + "[Stage 5] Response is not JSON, returning as is, take care!" + ) + return responseContentFinal + except Exception: + logger.warning(f"解压失败,原始响应: {responseContentDecrypted}") + raise SDGBResponseError("解压失败") + except SDGBRequestError as e: + logger.error(f"请求格式错误: {e}") + raise + except SDGBResponseError as e: + logger.warning(f"响应错误,将重试: {e}") + retries += 1 + time.sleep(2) + except Exception as e: + logger.warning(f"请求失败,将重试: {e}") + retries += 1 + time.sleep(2) + + finally: + if "httpClient" in locals(): + httpClient.close() + + raise SDGBApiError("重试多次仍然无法成功请求服务器") + + +def calcPlaySpecial(): + """使用 c_int32 实现的 SpecialNumber 算法""" + rng = random.SystemRandom() + num2 = rng.randint(1, 1037933) * 2069 + num2 += 1024 # GameManager.CalcSpecialNum() + num2 = c_int32(num2).value + result = c_int32(0) + for _ in range(32): + result.value <<= 1 + result.value += num2 & 1 + num2 >>= 1 + return c_int32(result.value).value + + +class AESPKCS7_2024: + # 实现了 maimai 通讯所用的 AES 加密的类 + def __init__(self, key: str, iv: str): + self.key = key.encode("utf-8") + self.iv = iv.encode("utf-8") + self.mode = AES.MODE_CBC + + # 加密 + def encrypt(self, content) -> bytes: + # if content is str, convert to bytes + if isinstance(content, str): + encodedData = content.encode("utf-8") + cipher = AES.new(self.key, self.mode, self.iv) + content_padded = pad(encodedData, AES.block_size) + encrypted_bytes = cipher.encrypt(content_padded) + return encrypted_bytes + + # 解密 + def decrypt(self, encrypted_content: bytes) -> str: + cipher = AES.new(self.key, self.mode, self.iv) + decrypted_padded = cipher.decrypt(encrypted_content) + decrypted = unpad(decrypted_padded, AES.block_size) + return decrypted + + +def apiSDGB_2024( + data: str, + targetApi: str, + userAgentExtraData: str, + noLog: bool = False, + timeout: int = 5, +): + """ + 舞萌DX 2024 API 通讯用函数 + :param data: 请求数据 + :param targetApi: 使用的 API + :param userAgentExtraData: UA 附加信息,机台相关则为狗号(如A63E01E9564),用户相关则为 UID + :param noLog: 是否不记录日志 + """ + maxRetries = 3 + agentExtra = str(userAgentExtraData) + aes = AESPKCS7_2024(AesKey, AesIV) + reqData_encrypted = aes.encrypt(data) + reqData_deflated = zlib.compress(reqData_encrypted) + endpoint = "https://maimai-gm.wahlap.com:42081/Maimai2Servlet/" + if not noLog: + logger.debug(f"开始请求 {targetApi},以 {data}") + + retries = 0 + while retries < maxRetries: + try: + if useProxy: + # 使用代理 + logger.debug("使用代理") + httpClient = httpx.Client(proxy=proxyUrl, verify=False) + else: + # 不使用代理 + logger.debug("不使用代理") + httpClient = httpx.Client(verify=False) + responseOriginal = httpClient.post( + url=endpoint + getSDGBApiHash(targetApi), + headers={ + "User-Agent": f"{getSDGBApiHash(targetApi)}#{agentExtra}", + "Content-Type": "application/json", + "Mai-Encoding": "1.40", + "Accept-Encoding": "", + "Charset": "UTF-8", + "Content-Encoding": "deflate", + "Expect": "100-continue", + }, + content=reqData_deflated, + # 经测试,加 Verify 之后速度慢好多,因此建议选择性开 + # verify=certifi.where(), + # verify=False, + timeout=timeout, + ) + + if not noLog: + logger.info(f"{targetApi} 请求结果: {responseOriginal.status_code}") + + if responseOriginal.status_code == 200: + logger.debug("200 OK!") + else: + errorMessage = f"请求失败: {responseOriginal.status_code}" + logger.error(errorMessage) + raise SDGBRequestError(errorMessage) + + responseRAWContent = responseOriginal.content + + try: + responseDecompressed = zlib.decompress(responseRAWContent) + logger.debug("成功解压响应!") + except Exception: + logger.warning(f"无法解压,得到的原始响应: {responseRAWContent}") + raise SDGBResponseError("解压失败") + try: + resultResponse = aes.decrypt(responseDecompressed) + logger.debug("成功解密响应!") + except Exception: + logger.warning(f"解密失败,得到的原始响应: {responseDecompressed}") + raise SDGBResponseError("解密失败") + + if not noLog: + logger.debug(f"响应: {resultResponse}") + return resultResponse + + # 异常处理 + except SDGBRequestError: + # 请求格式错误,不需要重试 + raise SDGBRequestError("请求格式错误") + except SDGBResponseError as e: + # 响应解析错误,这种有一定可能是我们的问题,所以只重试一次 + logger.warning(f"将重试一次 Resp Err: {e}") + retries += 2 + time.sleep(2) + except Exception as e: + # 其他错误,重试多次 + logger.warning(f"将开始重试请求. {e}") + retries += 1 + time.sleep(2) + + raise SDGBApiError("重试多次仍然无法成功请求服务器") diff --git a/backend/ActionChangeVersion.py b/backend/ActionChangeVersion.py new file mode 100644 index 0000000..764181a --- /dev/null +++ b/backend/ActionChangeVersion.py @@ -0,0 +1,55 @@ +# 改变版本号,实现伪封号和解封号之类 + +from loguru import logger + +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from HelperFullPlay import implFullPlayAction, generateMusicData +from HelperGetUserThing import implGetUser_ +from MyConfig import testUid8 + + +def implWipeTickets(userId: int, currentLoginTimestamp: int, currentLoginResult) -> str: + # Get User Charge + currentUserCharge = implGetUser_("Charge", userId) + + currentUserChargeList = currentUserCharge["userChargeList"] + # All stock set to 0 + for charge in currentUserChargeList: + charge["stock"] = 0 + + # example format + # {"userId":11088995,"length":16,"userChargeList":[{"chargeId":1,"stock":0,"purchaseDate":"2025-02-04 00:51:50","validDate":"2025-02-04 00:51:50","extNum1":0},{"chargeId":2,"stock":0,"purchaseDate":"2025-06-11 17:19:42","validDate":"2025-09-09 04:00:00","extNum1":0},{"chargeId":3,"stock":0,"purchaseDate":"2025-06-11 17:19:40","validDate":"2025-09-09 04:00:00","extNum1":0},{"chargeId":4,"stock":0,"purchaseDate":"2025-06-11 09:34:51","validDate":"2025-09-09 04:00:00","extNum1":0},{"chargeId":5,"stock":0,"purchaseDate":"2025-01-30 12:31:16","validDate":"2025-04-30 04:00:00","extNum1":0},{"chargeId":6,"stock":0,"purchaseDate":"2025-02-17 20:01:42","validDate":"2025-02-17 20:01:42","extNum1":0},{"chargeId":7,"stock":0,"purchaseDate":"2025-02-06 16:17:41","validDate":"2025-02-06 16:17:41","extNum1":0},{"chargeId":8,"stock":0,"purchaseDate":"2025-02-06 16:17:49","validDate":"2025-02-06 16:17:49","extNum1":0},{"chargeId":9,"stock":0,"purchaseDate":"2025-02-06 16:18:00","validDate":"2025-02-06 16:18:00","extNum1":0},{"chargeId":10001,"stock":1,"purchaseDate":"2025-06-11 17:19:51","validDate":"2025-09-09 04:00:00","extNum1":0},{"chargeId":10005,"stock":0,"purchaseDate":"2025-04-25 15:45:55","validDate":"2025-07-24 04:00:00","extNum1":0},{"chargeId":10105,"stock":0,"purchaseDate":"2025-04-25 15:46:00","validDate":"2025-07-24 04:00:00","extNum1":0},{"chargeId":10205,"stock":0,"purchaseDate":"2025-04-25 15:46:03","validDate":"2025-07-24 04:00:00","extNum1":0},{"chargeId":11001,"stock":0,"purchaseDate":"2025-01-08 20:43:05","validDate":"2025-04-08 04:00:00","extNum1":0},{"chargeId":30001,"stock":0,"purchaseDate":"2025-04-25 15:46:17","validDate":"2025-07-24 04:00:00","extNum1":0},{"chargeId":999999,"stock":0,"purchaseDate":"2025-02-06 23:03:14","validDate":"2025-02-06 23:03:14","extNum1":0}]} + + musicData = generateMusicData() + userAllPatches = { + "upsertUserAll": { + # "userData": [{ + # "lastRomVersion": romVersion, + # "lastDataVersion": dataVersion + # }], + "userChargeList": currentUserChargeList, + "userMusicDetailList": [musicData], + "isNewMusicDetailList": "1", # 1避免覆盖 + } + } + + result = implFullPlayAction( + userId, currentLoginTimestamp, currentLoginResult, musicData, userAllPatches + ) + return result + + +if __name__ == "__main__": + userId = testUid8 + currentLoginTimestamp = generateTimestamp() + loginResult = apiLogin(currentLoginTimestamp, userId) + + if loginResult["returnCode"] != 1: + logger.info("登录失败") + exit() + try: + logger.info(implWipeTickets(userId, currentLoginTimestamp, loginResult)) + logger.info(apiLogout(currentLoginTimestamp, userId)) + finally: + logger.info(apiLogout(currentLoginTimestamp, userId)) + # logger.warning("Error") diff --git a/backend/ActionLoginBonus.py b/backend/ActionLoginBonus.py new file mode 100644 index 0000000..f8bdc0b --- /dev/null +++ b/backend/ActionLoginBonus.py @@ -0,0 +1,156 @@ +# ログインボーナス!やったね! +# セガ秘 内部使用のみ(トレードマーク) + +import rapidjson as json +from loguru import logger +import xml.etree.ElementTree as ET + +from Config import ( + loginBonusDBPath, + loginBonusDBPathFallback, +) +from MyConfig import testUid +from API_TitleServer import apiSDGB +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from HelperFullPlay import implFullPlayAction, generateMusicData + + +class NoSelectedBonusError(Exception): + pass + + +def apiQueryLoginBonus(userId: int) -> str: + """ログインボーナスを取得する API""" + data = json.dumps({"userId": int(userId), "nextIndex": 0, "maxCount": 2000}) + return apiSDGB(data, "GetUserLoginBonusApi", userId) + + +def implLoginBonus( + userId: int, currentLoginTimestamp: int, currentLoginResult, bonusGenerateMode=1 +): + """ + ログインボーナスデータをアップロードする + bonusGenerateMode は、ログインボーナスを生成する方法を指定します。 + 1: 選択したボーナスのみ MAX にする(選択したボーナスはないの場合は False を返す) + 2: 全部 MAX にする + """ + musicData = generateMusicData() + # サーバーからログインボーナスデータを取得 + data = json.dumps({"userId": int(userId), "nextIndex": 0, "maxCount": 2000}) + UserLoginBonusResponse = json.loads(apiSDGB(data, "GetUserLoginBonusApi", userId)) + # ログインボーナスリストを生成、それから処理してアップロード + UserLoginBonusList = UserLoginBonusResponse["userLoginBonusList"] + finalBonusList = generateLoginBonusList(UserLoginBonusList, bonusGenerateMode) + if not finalBonusList: + return False # ログインボーナスを選択していないから失敗 + # UserAllのパッチ + userAllPatches = { + "upsertUserAll": { + "userMusicDetailList": [musicData], + "isNewMusicDetailList": "1", # 上書きしない + "userLoginBonusList": finalBonusList, + "isNewLoginBonusList": "0" * len(finalBonusList), + } + } + result = implFullPlayAction( + userId, currentLoginTimestamp, currentLoginResult, musicData, userAllPatches + ) + return result + + +def generateLoginBonusList(UserLoginBonusList, generateMode=1): + """ + ログインボーナスリストを生成します。 + generateMode は、ログインボーナスを生成する方法を指定します。 + 1: 選択したボーナスのみ MAX にする(選択したボーナスはないの場合は False を返す) + 2: 全部 MAX にする + """ + # HDDから、ログインボーナスデータを読み込む + # アップデートがある場合、このファイルを更新する必要があります + # 必ず最新のデータを使用してください + try: + tree = ET.parse(loginBonusDBPath) + root = tree.getroot() + loginBonusIdList = [ + int(item.find("id").text) for item in root.findall(".//StringID") + ] + logger.debug(f"ログインボーナスIDリスト: {loginBonusIdList}") + except FileNotFoundError: + try: + tree = ET.parse(loginBonusDBPathFallback) + root = tree.getroot() + loginBonusIdList = [ + int(item.find("id").text) for item in root.findall(".//StringID") + ] + logger.debug(f"ログインボーナスIDリスト: {loginBonusIdList}") + except Exception: + raise FileNotFoundError("ログインボーナスデータベースを読み込めません") + + # ログインボーナスの MAX POINT は5の場合があります + # その全部のボーナスIDをこのリストに追加してください + # 必ず最新のデータを使用してください + Bonus5Id = [12, 29, 30, 38, 43, 604, 611] + + # UserBonusList から bonusId を取得 + UserLoginBonusIdList = [item["bonusId"] for item in UserLoginBonusList] + + # 存在しないボーナス + NonExistingBonuses = list(set(loginBonusIdList) - set(UserLoginBonusIdList)) + logger.debug(f"存在しないボーナス: {NonExistingBonuses}") + + bonusList = [] + if generateMode == 1: # 選択したボーナスのみ MAX にする + for item in UserLoginBonusList: + if item["isCurrent"] and not item["isComplete"]: + point = 4 if item["bonusId"] in Bonus5Id else 9 + data = { + "bonusId": item["bonusId"], + "point": point, + "isCurrent": True, + "isComplete": False, + } + bonusList.append(data) + if len(bonusList) == 0: + raise NoSelectedBonusError("選択したログインボーナスがありません") + elif generateMode == 2: # 全部 MAX にする + # 存在しているボーナスを追加 + for item in UserLoginBonusList: + if not item["isComplete"]: + data = { + "bonusId": item["bonusId"], + "point": 4 if item["bonusId"] in Bonus5Id else 9, + "isCurrent": item["isCurrent"], + "isComplete": False, + } + bonusList.append(data) + elif item["bonusId"] == 999: + data = { + "bonusId": 999, + "point": (item["point"] // 10) * 10 + 9, + "isCurrent": item["isCurrent"], + "isComplete": False, + } + bonusList.append(data) + # 存在しないボーナスを追加 + for bonusId in NonExistingBonuses: + data = { + "bonusId": bonusId, + "point": 4 if bonusId in Bonus5Id else 9, + "isCurrent": False, + "isComplete": False, + } + bonusList.append(data) + else: + raise SyntaxError("generateMode は 1 または 2 でなければなりません") + + logger.debug(f"ログインボーナスリスト: {bonusList}") + return bonusList + + +if __name__ == "__main__": + # ログインボーナスデータをアップロードする + userId = testUid + currentLoginTimestamp = generateTimestamp() + currentLoginResult = apiLogin(currentLoginTimestamp, userId) + implLoginBonus(userId, currentLoginTimestamp, currentLoginResult, 2) + apiLogout(currentLoginTimestamp, userId) diff --git a/backend/ActionScoreRecord.py b/backend/ActionScoreRecord.py new file mode 100644 index 0000000..345804d --- /dev/null +++ b/backend/ActionScoreRecord.py @@ -0,0 +1,99 @@ +# 删除和上传成绩 + +from loguru import logger + +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from HelperFullPlay import implFullPlayAction +from MyConfig import testUid + + +def implDeleteMusicRecord( + userId: int, + currentLoginTimestamp: int, + currentLoginResult, + musicId: int, + levelId: int, +) -> str: + musicData = { + "musicId": musicId, + "level": levelId, + "playCount": 1, + "achievement": 0, + "comboStatus": 0, + "syncStatus": 0, + "deluxscoreMax": 0, + "scoreRank": 0, + "extNum1": 0, + } + userAllPatches = { + "upsertUserAll": { + "userMusicDetailList": [musicData], + "isNewMusicDetailList": "0", # 0为编辑,即可删除掉成绩 + } + } + result = implFullPlayAction( + userId, currentLoginTimestamp, currentLoginResult, musicData, userAllPatches + ) + return result + + +def implUploadMusicRecord( + userId: int, + currentLoginTimestamp: int, + currentLoginResult, + musicId: int, + levelId: int, + achievement: int, + dxScore: int, +) -> str: + """ + VERY EARLY STAGE OF UPLOADING SCORES!!!! DO NOT USE THIS!!!! + 上传成绩的参考实现。 + """ + + # 要上传的数据 + musicData = { + "musicId": musicId, + "level": levelId, + "playCount": 1, + "achievement": achievement, + "comboStatus": 0, + "syncStatus": 0, + "deluxscoreMax": dxScore, + "scoreRank": 0, + "extNum1": 0, + } + userAllPatches = { + "upsertUserAll": { + "userMusicDetailList": [musicData], + "isNewMusicDetailList": "1", # 0编辑 1插入 + } + } + result = implFullPlayAction( + userId, currentLoginTimestamp, currentLoginResult, musicData, userAllPatches + ) + return result + + +if __name__ == "__main__": + userId = testUid + currentLoginTimestamp = generateTimestamp() + loginResult = apiLogin(currentLoginTimestamp, userId) + + musicId = 852 # 852 is tiamat + levelId = 3 # 3 is MASTER + + if loginResult["returnCode"] != 1: + logger.info("登录失败") + exit() + try: + logger.info( + implDeleteMusicRecord( + userId, currentLoginTimestamp, loginResult, musicId, levelId + ) + ) + # logger.info(implUploadMusicRecord(userId, currentLoginTimestamp, loginResult, musicId, levelId, 1000000, 100)) + logger.info(apiLogout(currentLoginTimestamp, userId)) + finally: + logger.info(apiLogout(currentLoginTimestamp, userId)) + # logger.warning("Error") diff --git a/backend/ActionUnlockItem.py b/backend/ActionUnlockItem.py new file mode 100644 index 0000000..04302aa --- /dev/null +++ b/backend/ActionUnlockItem.py @@ -0,0 +1,71 @@ +# 解锁一些东西的外部代码 + +from loguru import logger + +from MyConfig import testUid8 +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from HelperUnlockThing import implUnlockThing + + +def implUnlockMultiItem( + itemKind: int, + userId: int, + currentLoginTimestamp: int, + currentLoginResult, + *itemIds: int, +) -> str: + if not itemIds: + logger.info("无操作,跳过处理!") + return + """ + 发单个东西,比如搭档 10 + """ + userItemList = [ + {"itemKind": itemKind, "itemId": itemId, "stock": 1, "isValid": True} + for itemId in itemIds + ] + unlockThingResult = implUnlockThing( + userItemList, userId, currentLoginTimestamp, currentLoginResult + ) + return unlockThingResult + + +def implUnlockMusic( + musicToBeUnlocked: int, userId: int, currentLoginTimestamp: int, currentLoginResult +) -> str: + """ + 解锁乐曲 + """ + userItemList = [ + {"itemKind": 5, "itemId": musicToBeUnlocked, "stock": 1, "isValid": True}, + {"itemKind": 6, "itemId": musicToBeUnlocked, "stock": 1, "isValid": True}, + ] + unlockThingResult = implUnlockThing( + userItemList, userId, currentLoginTimestamp, currentLoginResult + ) + return unlockThingResult + + +if __name__ == "__main__": + userId = int(input("type user id: ").strip() or "0") or testUid8 + currentLoginTimestamp = generateTimestamp() + loginResult = apiLogin(currentLoginTimestamp, userId) + + if loginResult["returnCode"] != 1: + logger.info("登录失败") + exit() + try: + items = range(11, 33) # all partners + logger.info( + implUnlockMultiItem( + 10, + userId, + currentLoginTimestamp, + loginResult, + *items, + ) + ) + logger.info(apiLogout(currentLoginTimestamp, userId)) + finally: + logger.info(apiLogout(currentLoginTimestamp, userId)) + # logger.warning("Error") diff --git a/backend/Best50_To_Diving_Fish.py b/backend/Best50_To_Diving_Fish.py new file mode 100644 index 0000000..dd1aad1 --- /dev/null +++ b/backend/Best50_To_Diving_Fish.py @@ -0,0 +1,199 @@ +import requests +from loguru import logger + +from HelperGetUserMusicDetail import getUserFullMusicDetail +from HelperMusicDB import getMusicTitle + + +class divingFishAuthFailError(Exception): + pass + + +class divingFishCommError(Exception): + pass + + +# 水鱼查分器的 API 地址 +BASE_URL = "https://www.diving-fish.com/api/maimaidxprober" + +# 水鱼查分器的成绩状态转换 +COMBO_ID_TO_NAME = ["", "fc", "fcp", "ap", "app"] +SYNC_ID_TO_NAME = ["", "fs", "fsp", "fsd", "fsdp", "sync"] + + +def apiDivingFish(method: str, apiPath: str, importToken: str, data=None): + """水鱼查分器的 API 通讯实现""" + headers = {"Import-Token": importToken} + if method == "POST": + headers["Content-Type"] = "application/json" + logger.info(f"水鱼查分器 API 请求:{method} {BASE_URL + apiPath}") + if method == "POST": + response = requests.post( + url=BASE_URL + apiPath, + json=data, + headers=headers, + ) + elif method == "GET": + response = requests.get( + url=BASE_URL + apiPath, + headers=headers, + ) + elif method == "DELETE": + response = requests.delete( + url=BASE_URL + apiPath, + headers=headers, + ) + else: + logger.error(f"未知的请求方法:{method}") + raise ValueError(f"未知的请求方法:{method}") + + logger.info(f"水鱼查分器请求结果:{response.status_code}") + logger.debug(f"水鱼查分器回应:{response.text}") + finalResponseTextDecode = response.text.encode("utf-8").decode("unicode_escape") + logger.debug(f"水鱼查分器回应解码后:{finalResponseTextDecode}") + match response.status_code: + case 200: + return response.json() + case 500: + raise divingFishAuthFailError + case _: + raise divingFishCommError + + +def getFishRecords(importToken: str) -> dict: + """获取水鱼查分器的成绩""" + return apiDivingFish("GET", "/player/records", importToken) + + +def updateFishRecords(importToken: str, records: list[dict]) -> dict: + """上传成绩到水鱼查分器""" + return apiDivingFish("POST", "/player/update_records", importToken, records) + + +def resetFishRecords(fishImportToken: str): + """重置水鱼查分器的用户数据""" + return apiDivingFish("DELETE", "/player/delete_records", fishImportToken) + + +def getFishUserInfo(userQQ: int): + """按QQ获取水鱼查分器的用户信息""" + return apiDivingFish("POST", "/query/player", "", {"qq": userQQ}) + + +def maimaiUserMusicDetailToDivingFishFormat(userMusicDetailList) -> list: + """舞萌的 UserMusicDetail 成绩格式转换成水鱼的格式""" + divingFishList = [] + for currentMusicDetail in userMusicDetailList: + # musicId 大于 100000 属于宴谱,不计入 + if currentMusicDetail["musicId"] >= 100000: + continue + # 获得歌名 + currentMusicTitle = getMusicTitle(currentMusicDetail["musicId"]) + # 如果数据库里未找到此歌曲 + if currentMusicTitle == "R_ERR_MUSIC_ID_NOT_IN_DATABASE": + logger.warning(f"数据库无此歌曲 跳过: {currentMusicDetail['musicId']}") + continue + # 每一个乐曲都判断下是 DX 还是标准 + if currentMusicDetail["musicId"] >= 10000: + notesType = "DX" + else: + notesType = "SD" + # 追加进列表 + try: + divingFishList.append( + { + "achievements": ( + currentMusicDetail["achievement"] / 10000 + ), # 水鱼的成绩是 float 而非舞萌的 int + "title": currentMusicTitle, + "type": notesType, + "level_index": currentMusicDetail["level"], + "fc": COMBO_ID_TO_NAME[currentMusicDetail["comboStatus"]], + "fs": SYNC_ID_TO_NAME[currentMusicDetail["syncStatus"]], + "dxScore": currentMusicDetail["deluxscoreMax"], + } + ) + except Exception: + logger.error(f"无法将 UserMusic 翻译成水鱼格式: {currentMusicDetail}") + + return divingFishList + + +def isVaildFishToken(importToken: str): + """通过尝试获取一次成绩,检查水鱼查分器的 Token 是否有效 + 有效返回 True,无效返回 False""" + result = apiDivingFish("GET", "/player/records", importToken) + logger.debug(f"水鱼查分器 Token 检查结果:{result}") + if result: + return True + return False + + +def implGetUserCurrentDXRating(userQQ: int): + """获取用户当前的 DX RATING""" + try: + playerData = getFishUserInfo(userQQ) + playerRating = playerData["rating"] + logger.info(f"用户 {userQQ} 的 DX RATING 是 {playerRating}") + except Exception as e: + logger.warning(f"无法获取用户 {userQQ} 的 DX RATING: {e}") + return False + return playerRating + + +def implUserMusicToDivingFish(userId: int, fishImportToken: str): + """上传所有成绩到水鱼的参考实现。 + 返回一个 int 的 ErrorCode。 + 0: Success + 1: Get User Music Fail + 2: Auth Fail + 3: Comm Error + """ + logger.info("开始尝试上传舞萌成绩到水鱼查分器!") + try: + userFullMusicDetailList = getUserFullMusicDetail(userId) + logger.info("成功得到成绩!转换成水鱼格式..") + divingFishData = maimaiUserMusicDetailToDivingFishFormat( + userFullMusicDetailList + ) + logger.info("转换成功!开始上传水鱼..") + except Exception as e: + logger.error(f"获取成绩失败!{e}") + return 1 + try: + updateFishRecords(fishImportToken, divingFishData) + except divingFishAuthFailError: + logger.error("水鱼查分器认证失败!") + return 2 + except divingFishCommError: + logger.error("水鱼查分器通讯失败!") + return 3 + + +def generateDebugTestScore(): + """生成测试成绩""" + return [ + { + "achievement": 1010000, + "comboStatus": 4, + "deluxscoreMax": 4026, + "level": 4, + "musicId": 834, + "syncStatus": 4, + }, + { + "achievement": 1010000, + "comboStatus": 4, + "deluxscoreMax": 4200, + "level": 4, + "musicId": 11663, + "syncStatus": 4, + }, + ] + + +if __name__ == "__main__": + userId = int(input("userId: ")) + fishImportToken = input("DivingFish Token: ") + + implUserMusicToDivingFish(userId, fishImportToken) diff --git a/backend/ChargeTicket.py b/backend/ChargeTicket.py new file mode 100644 index 0000000..d411d37 --- /dev/null +++ b/backend/ChargeTicket.py @@ -0,0 +1,126 @@ +from datetime import datetime, timedelta + +# 倍票相关 API 的实现 +import rapidjson as json +import pytz +from loguru import logger + +from API_TitleServer import apiSDGB +from HelperGetUserThing import implGetUser_ + +from Config import ( + clientId, + placeId, + regionId, +) +from MyConfig import testUid2 + +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from HelperFullPlay import implFullPlayAction, generateMusicData + + +def implWipeTickets(userId: int, currentLoginTimestamp: int, currentLoginResult) -> str: + """清空用户所有票的 API 请求器,返回 Json String。""" + # 先得到当前用户的 Charge 数据 + currentUserCharge = implGetUser_("Charge", userId) + # 取得 List + currentUserChargeList = currentUserCharge["userChargeList"] + # 所有 stock 都置为 0 + for charge in currentUserChargeList: + charge["stock"] = 0 + + musicData = generateMusicData() + userAllPatches = { + "upsertUserAll": { + "userChargeList": currentUserChargeList, + "userMusicDetailList": [musicData], + "isNewMusicDetailList": "1", # 1避免覆盖 + } + } + + result = implFullPlayAction( + userId, currentLoginTimestamp, currentLoginResult, musicData, userAllPatches + ) + return result + + +def apiQueryTicket(userId: int) -> str: + """查询已有票的 API 请求器,返回 Json String。""" + # 构建 Payload + data = json.dumps({"userId": userId}) + # 发送请求 + userdata_result = apiSDGB(data, "GetUserChargeApi", userId) + # 返回响应 + return userdata_result + + +def apiBuyTicket( + userId: int, ticketType: int, price: int, playerRating: int, playCount: int +) -> str: + """倍票购买 API 的请求器""" + + nowTime = datetime.now(pytz.timezone("Asia/Shanghai")) + + # 构造请求数据 Payload + data = json.dumps( + { + "userId": userId, + "userChargelog": { + "chargeId": ticketType, + "price": price, + "purchaseDate": nowTime.strftime("%Y-%m-%d %H:%M:%S.0"), + "playCount": playCount, + "playerRating": playerRating, + "placeId": placeId, + "regionId": regionId, + "clientId": clientId, + }, + "userCharge": { + "chargeId": ticketType, + "stock": 1, + "purchaseDate": nowTime.strftime("%Y-%m-%d %H:%M:%S.0"), + "validDate": (nowTime + timedelta(days=90)) + .replace(hour=4, minute=0, second=0) + .strftime("%Y-%m-%d %H:%M:%S"), + }, + } + ) + # 发送请求,返回最终得到的 Json String 回执 + return apiSDGB(data, "UpsertUserChargelogApi", userId) + + +def implBuyTicket(userId: int, ticketType: int): + """ + 购买倍票 API 的参考实现。 + 需要事先登录. + 返回服务器响应的 Json string。 + """ + # 先使用 GetUserData API 请求器,取得 rating 和 pc 数 + currentUserData = implGetUser_("Data", userId) + if currentUserData: + playerRating = currentUserData["userData"]["playerRating"] + playCount = currentUserData["userData"].get("playCount", 0) + else: + return False + # 正式买票 + getTicketResponseStr = apiBuyTicket( + userId, ticketType, ticketType - 1, playerRating, playCount + ) + # 返回结果 + return getTicketResponseStr + + +if __name__ == "__main__": + userId = testUid2 + currentLoginTimestamp = generateTimestamp() + loginResult = apiLogin(currentLoginTimestamp, userId) + + if loginResult["returnCode"] != 1: + logger.info("登录失败") + exit() + try: + logger.info(implBuyTicket(userId, 2)) # 购买倍票 + # logger.info(apiQueryTicket(userId)) + finally: + logger.info(apiLogout(currentLoginTimestamp, userId)) + # logger.warning("Error") diff --git a/backend/Config.py b/backend/Config.py new file mode 100644 index 0000000..bc465ed --- /dev/null +++ b/backend/Config.py @@ -0,0 +1,16 @@ +regionId = 13 +regionName = "河南" +placeId = 2411 +placeName = "智游星期六河南郑州店" +clientId = "A63E01E6154" + +useProxy = False +proxyUrl = "http://100.104.133.113:33080" + +loginBonusDBPath = "./Data/loginBonusDB.xml" +musicDBPath = "./Data/musicDB.json" + +loginBonusDBPathFallback = "./maimaiDX-Api/Data/loginBonusDB.xml" +musicDBPathFallback = "./maimaiDX-Api/Data/musicDB.json" + +# 日本精工,安全防漏 diff --git a/backend/Data/loginBonusDB.xml b/backend/Data/loginBonusDB.xml new file mode 100644 index 0000000..4df7acd --- /dev/null +++ b/backend/Data/loginBonusDB.xml @@ -0,0 +1,318 @@ + + + loginBonus + + + 53 + パートナー:ソルト(ぷりずむ) + + + 48 + パートナー:ラズ(ばでぃーず) + + + 38 + パートナー:ずんだもん + + + 39 + パートナー:乙姫(ばでぃーず) + + + 40 + パートナー:らいむっくま&れもんっくま(ばでぃーず) + + + 34 + パートナー:黒姫 + + + 24 + パートナー:ラズ(ふぇすてぃばる) + + + 25 + パートナー:シフォン(ふぇすてぃばる) + + + 26 + パートナー:ソルト(ふぇすてぃばる) + + + 19 + パートナー:ちびみるく + + + 20 + パートナー:百合咲ミカ + + + 8 + パートナー:しゃま(ゆにばーす) + + + 9 + パートナー:みるく(ゆにばーす) + + + 7 + パートナー:乙姫(すぷらっしゅ) + + + 1 + パートナー:乙姫 + + + 2 + パートナー:ラズ + + + 3 + パートナー:シフォン + + + 4 + パートナー:ソルト + + + 5 + パートナー:しゃま + + + 6 + パートナー:みるく + + + 54 + でらっくす譜面:One Step Ahead + + + 55 + でらっくす譜面:天狗の落とし文 feat. ytr + + + 612 + でらっくす譜面:LANCE + + + 613 + でらっくす譜面:HYP3RTRIBE + + + 614 + でらっくす譜面:Imitation:Loud Lounge + + + 49 + でらっくす譜面:ハッピーシンセサイザ + + + 50 + でらっくす譜面:sweet little sister + + + 608 + でらっくす譜面:御旗のもとに + + + 609 + でらっくす譜面:炎歌 -ほむらうた- + + + 610 + でらっくす譜面:華の集落、秋のお届け + + + 605 + でらっくす譜面:oboro + + + 606 + でらっくす譜面:ナミダと流星 + + + 607 + スタンダード譜面:渦状銀河のシンフォニエッタ + + + 508 + でらっくす譜面:Latent Kingdom + + + 41 + でらっくす譜面:初音ミクの消失 + + + 42 + でらっくす譜面:色は匂へど散りぬるを + + + 601 + でらっくす譜面:BULK UP (GAME EXCLUSIVE EDIT) + + + 602 + でらっくす譜面:Monochrome Rainbow + + + 603 + でらっくす譜面:Selector + + + 35 + でらっくす譜面:深海少女 + + + 36 + でらっくす譜面:ナイト・オブ・ナイツ + + + 27 + でらっくす譜面:M.S.S.Planet + + + 28 + でらっくす譜面:響縁 + + + 501 + スタンダード譜面:Halcyon + + + 502 + スタンダード譜面:サンバランド + + + 503 + でらっくす譜面:Starlight Disco + + + 504 + でらっくす譜面:火炎地獄 + + + 505 + スタンダード譜面:VIIIbit Explorer + + + 506 + でらっくす譜面:Maxi + + + 507 + でらっくす譜面:ケロ⑨destiny + + + 21 + でらっくす譜面:セツナトリップ + + + 22 + でらっくす譜面:Grip & Break down !! + + + 17 + でらっくす譜面:ゴーストルール + + + 18 + でらっくす譜面:taboo tears you up + + + 56 + アイコン:PRiSM + + + 611 + アイコン:BUDDiES 乙姫&ラズ + + + 43 + アイコン:BUDDiES + + + 604 + アイコン:FESTiVAL ラズ&シフォン&ソルト + + + 29 + アイコン:FESTiVAL + + + 30 + アイコン:Lia=Fail + + + 12 + アイコン:UNiVERSE + + + 57 + ネームプレート:QuiQ + + + 51 + ネームプレート:Swift Swing + + + 14 + ネームプレート:はっぴー(ゆにばーす) + + + 58 + フレーム:Valsqotch + + + 52 + フレーム:Latent Kingdom + + + 44 + フレーム:mystique as iris + + + 45 + フレーム:VeRForTe αRtE:VEiN + + + 37 + フレーム:Tricolor⁂circuS + + + 31 + フレーム:Heavenly Blast + + + 32 + フレーム:sølips + + + 33 + フレーム:Rainbow Rush Story + + + 23 + フレーム:ふたりでばかんすにゃ♪ + + + 15 + フレーム:ここからはじまるプロローグ。 + + + 16 + フレーム:モ゜ルモ゜ル + + + 10 + フレーム:黒姫 + + + 11 + フレーム:百合咲ミカ + + + 999 + ちほー進行1.5倍チケット + + + \ No newline at end of file diff --git a/backend/Data/musicDB.json b/backend/Data/musicDB.json new file mode 100644 index 0000000..4b4840f --- /dev/null +++ b/backend/Data/musicDB.json @@ -0,0 +1,1597 @@ +{ + "8": {"name": "True Love Song", "version": 0}, + "9": {"name": "Color My World", "version": 0}, + "10": {"name": "LOVE & JOY", "version": 0}, + "11": {"name": "じょいふる", "version": 0}, + "15": {"name": "Everyday、カチューシャ", "version": 0}, + "17": {"name": "Future", "version": 0}, + "18": {"name": "Love You", "version": 0}, + "20": {"name": "恋愛サーキュレーション", "version": 0}, + "21": {"name": "コネクト", "version": 0}, + "22": {"name": "In Chaos", "version": 0}, + "23": {"name": "Crush On You", "version": 0}, + "24": {"name": "Sun Dance", "version": 0}, + "25": {"name": "Sweets×Sweets", "version": 0}, + "27": {"name": "Love or Lies", "version": 0}, + "29": {"name": "VICTORY", "version": 3}, + "30": {"name": "ネコ日和。", "version": 0}, + "31": {"name": "虹と太陽", "version": 0}, + "34": {"name": "Garden", "version": 0}, + "35": {"name": "炭★坑★節", "version": 0}, + "38": {"name": "NIGHT OF FIRE", "version": 0}, + "40": {"name": "come again", "version": 0}, + "41": {"name": "Chu Chu", "version": 0}, + "42": {"name": "jelly", "version": 0}, + "43": {"name": "愛を止めないで", "version": 0}, + "44": {"name": "ハッピーシンセサイザ", "version": 0}, + "45": {"name": "Choo Choo TRAIN", "version": 2}, + "46": {"name": "ルカルカ★ナイトフィーバー", "version": 0}, + "47": {"name": "源平大戦絵巻テーマソング", "version": 0}, + "49": {"name": "アイデンティティ", "version": 0}, + "53": {"name": "美しく燃える森", "version": 0}, + "54": {"name": "ヘビーローテーション", "version": 0}, + "55": {"name": "CAN'T GET BACK", "version": 0}, + "56": {"name": "ウッーウッーウマウマ(゚∀゚)", "version": 0}, + "58": {"name": "Endless World", "version": 1}, + "59": {"name": "PON PON PON", "version": 0}, + "60": {"name": "New World", "version": 0}, + "61": {"name": "Beat Of Mind", "version": 0}, + "62": {"name": "檄!帝国華撃団(改)", "version": 0}, + "63": {"name": "メランコリック", "version": 0}, + "64": {"name": "教えて!! 魔法のLyric", "version": 0}, + "65": {"name": "ZIGG-ZAGG", "version": 0}, + "66": {"name": "ワールズエンド・ダンスホール", "version": 0}, + "67": {"name": "パンダヒーロー", "version": 0}, + "68": {"name": "オレンジの夏", "version": 0}, + "69": {"name": "BaBan!! -甘い罠-", "version": 0}, + "70": {"name": "ジングルベル", "version": 0}, + "71": {"name": "マトリョシカ", "version": 0}, + "73": {"name": "メグメグ☆ファイアーエンドレスナイト", "version": 0}, + "75": {"name": "天国と地獄", "version": 0}, + "76": {"name": "きみのためなら死ねる", "version": 0}, + "77": {"name": "怪盗Rのテーマ", "version": 0}, + "78": {"name": "マリアをはげませ", "version": 0}, + "79": {"name": "SHOW TIME", "version": 0}, + "80": {"name": "City Escape: Act1", "version": 0}, + "81": {"name": "Rooftop Run: Act1", "version": 0}, + "82": {"name": "Reach For The Stars", "version": 0}, + "83": {"name": "Urban Crusher [Remix]", "version": 0}, + "84": {"name": "Catch The Future", "version": 0}, + "85": {"name": "JACKY [Remix]", "version": 0}, + "100": {"name": "Tell Your World", "version": 1}, + "101": {"name": "おちゃめ機能", "version": 1}, + "102": {"name": "ダンシング☆サムライ", "version": 1}, + "106": {"name": "BAD∞END∞NIGHT", "version": 1}, + "107": {"name": "ロミオとシンデレラ", "version": 1}, + "108": {"name": "Sweetiex2", "version": 1}, + "109": {"name": "腐れ外道とチョコレゐト", "version": 1}, + "110": {"name": "Quartet Theme [Reborn]", "version": 1}, + "111": {"name": "Sky High [Reborn]", "version": 1}, + "112": {"name": "Like the Wind [Reborn]", "version": 1}, + "113": {"name": "YA・DA・YO [Reborn]", "version": 1}, + "114": {"name": "Space Harrier Main Theme [Reborn]", "version": 1}, + "115": {"name": "DADDY MULK -Groove remix-", "version": 2}, + "116": {"name": "ぴぴぱぷぅ!", "version": 1}, + "117": {"name": "炎歌 -ほむらうた-", "version": 1}, + "118": {"name": "犬日和。", "version": 1}, + "119": {"name": "ソーラン☆節", "version": 1}, + "120": {"name": "デコボコ体操第二", "version": 1}, + "121": {"name": "スイートマジック", "version": 1}, + "122": {"name": "ゴーゴー幽霊船", "version": 1}, + "125": {"name": "千本桜", "version": 1}, + "126": {"name": "つけまつける", "version": 1}, + "127": {"name": "Get Off of My Way", "version": 1}, + "128": {"name": "御旗のもとに", "version": 1}, + "130": {"name": "FEEL ALIVE", "version": 1}, + "131": {"name": "Link", "version": 1}, + "132": {"name": "Turn around", "version": 1}, + "133": {"name": "We Gonna Party", "version": 1}, + "134": {"name": "Night Fly", "version": 1}, + "135": {"name": "Feel My Fire", "version": 1}, + "136": {"name": "Streak", "version": 1}, + "137": {"name": "Spin me harder", "version": 1}, + "138": {"name": "Lionheart", "version": 1}, + "139": {"name": "Acceleration", "version": 1}, + "140": {"name": "Sprintrances", "version": 1}, + "141": {"name": "Nerverakes", "version": 1}, + "142": {"name": "Black Out", "version": 1}, + "143": {"name": "Fragrance", "version": 1}, + "144": {"name": "air's gravity", "version": 1}, + "145": {"name": "Starlight Disco", "version": 1}, + "146": {"name": "39", "version": 1}, + "147": {"name": "地上の戦士", "version": 1}, + "148": {"name": "サブリナ", "version": 1}, + "149": {"name": "SPELL MAGIC", "version": 1}, + "152": {"name": "超絶!Superlative", "version": 1}, + "153": {"name": "采配の刻 Power of order", "version": 1}, + "154": {"name": "DO RORO DERODERO ON DO RORO", "version": 1}, + "155": {"name": "泣き虫O'clock", "version": 1}, + "157": {"name": "Natural Flow", "version": 1}, + "159": {"name": "maimaiちゃんのテーマ", "version": 1}, + "161": {"name": "CANDY CANDY", "version": 1}, + "164": {"name": "READY STEADY GO", "version": 2}, + "165": {"name": "リライト", "version": 2}, + "166": {"name": "カルマ", "version": 2}, + "167": {"name": "GO!!!", "version": 2}, + "168": {"name": "LOVEドッきゅん♥", "version": 2}, + "169": {"name": "galaxias!", "version": 2}, + "172": {"name": "女々しくて", "version": 3}, + "173": {"name": "サラバ、愛しき悲しみたちよ", "version": 2}, + "174": {"name": "創聖のアクエリオン", "version": 2}, + "176": {"name": "Butter-Fly", "version": 2}, + "181": {"name": "君の知らない物語", "version": 2}, + "182": {"name": "残酷な天使のテーゼ", "version": 2}, + "184": {"name": "I ♥", "version": 2}, + "185": {"name": "*ハロー、プラネット。", "version": 2}, + "186": {"name": "イアイア★ナイトオブデザイア", "version": 2}, + "187": {"name": "ローリンガール", "version": 2}, + "188": {"name": "天ノ弱", "version": 2}, + "189": {"name": "弱虫モンブラン", "version": 2}, + "190": {"name": "モザイクロール", "version": 2}, + "191": {"name": "脳漿炸裂ガール", "version": 2}, + "192": {"name": "SPiCa", "version": 3}, + "193": {"name": "セツナトリップ", "version": 2}, + "194": {"name": "放課後ストライド", "version": 2}, + "198": {"name": "カゲロウデイズ", "version": 2}, + "199": {"name": "チルノのパーフェクトさんすう教室", "version": 2}, + "200": {"name": "Bad Apple!! feat nomico", "version": 2}, + "201": {"name": "魔理沙は大変なものを盗んでいきました", "version": 2}, + "202": {"name": "Grip & Break down !!", "version": 2}, + "203": {"name": "Help me, ERINNNNNN!!", "version": 2}, + "204": {"name": "ナイト・オブ・ナイツ", "version": 2}, + "205": {"name": "全人類ノ非想天則", "version": 2}, + "206": {"name": "しゅわスパ大作戦☆", "version": 2}, + "207": {"name": "時空を超えて久しぶり!", "version": 2}, + "208": {"name": "Her Dream Is To Be A Fantastic Sorceress", "version": 2}, + "209": {"name": "ココロスキャンのうた", "version": 2}, + "212": {"name": "神室雪月花", "version": 2}, + "213": {"name": "KONNANじゃないっ!", "version": 2}, + "214": {"name": "awake", "version": 2}, + "216": {"name": "Mysterious Destiny", "version": 2}, + "217": {"name": "Riders Of The Light", "version": 2}, + "218": {"name": "Terminal Storm", "version": 2}, + "219": {"name": "記憶、記録", "version": 2}, + "220": {"name": "セガサターン起動音[H.][Remix]", "version": 2}, + "223": {"name": "夜咄ディセイブ", "version": 2}, + "224": {"name": "ウサテイ", "version": 2}, + "225": {"name": "sweet little sister", "version": 3}, + "226": {"name": "Blew Moon", "version": 2}, + "227": {"name": "Garakuta Doll Play", "version": 2}, + "228": {"name": "Dreampainter", "version": 4}, + "229": {"name": "ぐるぐるWASH!コインランドリー・ディスコ", "version": 3}, + "230": {"name": "CYCLES", "version": 3}, + "231": {"name": "Heartbeats", "version": 3}, + "232": {"name": "Life Feels Good", "version": 3}, + "233": {"name": "MYTHOS", "version": 3}, + "234": {"name": "End of Twilight", "version": 3}, + "235": {"name": "火炎地獄", "version": 3}, + "236": {"name": "ユビキリ", "version": 9}, + "237": {"name": "Pixel Voyage", "version": 2}, + "238": {"name": "Get Happy", "version": 2}, + "239": {"name": "System “Z”", "version": 2}, + "240": {"name": "Beat of getting entangled", "version": 2}, + "241": {"name": "Cosmic Train", "version": 2}, + "242": {"name": "ナミダと流星", "version": 5}, + "244": {"name": "おても☆Yan", "version": 4}, + "246": {"name": "maiム・maiム feat.週刊少年マガジン", "version": 2}, + "247": {"name": "Danza zandA", "version": 3}, + "248": {"name": "Backyun! -悪い女-", "version": 2}, + "251": {"name": "Monochrome Rainbow", "version": 3}, + "252": {"name": "みんなのマイマイマー", "version": 2}, + "253": {"name": "円舞曲、君に", "version": 2}, + "254": {"name": "Live & Learn", "version": 3}, + "255": {"name": "Burning Hearts ~炎のANGEL~", "version": 2}, + "256": {"name": "いーあるふぁんくらぶ", "version": 2}, + "258": {"name": "Nyan Cat EX", "version": 2}, + "259": {"name": "ぽっぴっぽー", "version": 2}, + "260": {"name": "LUCIA", "version": 2}, + "261": {"name": "Death Scythe", "version": 2}, + "262": {"name": "BREAK YOU!!", "version": 2}, + "263": {"name": "JUMPIN' JUMPIN'", "version": 2}, + "264": {"name": "L'épilogue", "version": 2}, + "265": {"name": "Save This World νMIX", "version": 2}, + "266": {"name": "Living Universe", "version": 2}, + "267": {"name": "Ignite Infinity", "version": 2}, + "268": {"name": "トルコ行進曲 - オワタ\(^o^)/", "version": 2}, + "269": {"name": "裏表ラバーズ", "version": 2}, + "270": {"name": "Outlaw's Lullaby", "version": 3}, + "271": {"name": "Brand-new Japanesque", "version": 3}, + "274": {"name": "RPG", "version": 3}, + "277": {"name": "Tachyon", "version": 3}, + "278": {"name": "レッツゴー!陰陽師", "version": 3}, + "280": {"name": "炉心融解", "version": 8}, + "281": {"name": "ダブルラリアット", "version": 4}, + "282": {"name": "からくりピエロ", "version": 3}, + "283": {"name": "ネトゲ廃人シュプレヒコール", "version": 8}, + "284": {"name": "むかしむかしのきょうのぼく", "version": 3}, + "285": {"name": "magician's operation", "version": 3}, + "288": {"name": "六兆年と一夜物語", "version": 5}, + "289": {"name": "みくみくにしてあげる♪【してやんよ】", "version": 5}, + "290": {"name": "リリリリ★バーニングナイト", "version": 3}, + "291": {"name": "ハロ/ハワユ", "version": 3}, + "293": {"name": "Our Fighting", "version": 5}, + "295": {"name": "緋色のDance", "version": 4}, + "296": {"name": "明星ロケット", "version": 4}, + "297": {"name": "The Great Journey", "version": 4}, + "298": {"name": "お嫁にしなさいっ!", "version": 3}, + "299": {"name": "ロストワンの号哭", "version": 5}, + "300": {"name": "Sweet Devil", "version": 5}, + "301": {"name": "患部で止まってすぐ溶ける~狂気の優曇華院", "version": 3}, + "302": {"name": "ケロ⑨destiny", "version": 5}, + "303": {"name": "Endless, Sleepless Night", "version": 3}, + "305": {"name": "POP STAR", "version": 3}, + "308": {"name": "Back 2 Back", "version": 3}, + "309": {"name": "神々の祈り", "version": 3}, + "310": {"name": "エピクロスの虹はもう見えない", "version": 3}, + "311": {"name": "シアワセうさぎ", "version": 3}, + "312": {"name": "ってゐ! ~えいえんてゐVer~", "version": 5}, + "313": {"name": "待チ人ハ来ズ。", "version": 3}, + "314": {"name": "Cosmic Magic Shooter", "version": 4}, + "315": {"name": "深海少女", "version": 3}, + "316": {"name": "M.S.S.Planet", "version": 3}, + "318": {"name": "東方スイーツ!~鬼畜姉妹と受難メイド~", "version": 5}, + "319": {"name": "幻想のサテライト", "version": 3}, + "320": {"name": "最速最高シャッターガール", "version": 3}, + "321": {"name": "バラライカ", "version": 3}, + "322": {"name": "林檎華憐歌", "version": 3}, + "323": {"name": "YATTA!", "version": 3}, + "324": {"name": "Jack-the-Ripper◆", "version": 3}, + "325": {"name": "Windy Hill -Zone 1", "version": 3}, + "326": {"name": "コトバ・カラフル", "version": 3}, + "327": {"name": "タカハせ!名人マン", "version": 3}, + "328": {"name": "言ノ葉カルマ", "version": 3}, + "329": {"name": "かせげ!ジャリンコヒーロー", "version": 3}, + "330": {"name": "キズナの物語", "version": 3}, + "331": {"name": "Heart Beats", "version": 4}, + "332": {"name": "アゲアゲアゲイン", "version": 3}, + "333": {"name": "GO MY WAY!!", "version": 3}, + "334": {"name": "THE IDOLM@STER 2nd-mix", "version": 3}, + "335": {"name": "The world is all one !!", "version": 3}, + "336": {"name": "紅蓮の弓矢", "version": 3}, + "337": {"name": "鼓動", "version": 3}, + "338": {"name": "リクライム", "version": 3}, + "339": {"name": "DRAGONLADY", "version": 3}, + "340": {"name": "恋のヒメヒメぺったんこ", "version": 3}, + "341": {"name": "二息歩行", "version": 3}, + "342": {"name": "ファンタジーゾーン OPA-OPA! -GMT remix-", "version": 3}, + "343": {"name": "電車で電車でGO!GO!GO!GC! -GMT remix-", "version": 3}, + "344": {"name": "RIDGE RACER STEPS -GMT remix-", "version": 3}, + "345": {"name": "リッジでリッジでGO!GO!GO! -GMT mashup-", "version": 3}, + "346": {"name": "オパ! オパ! RACER -GMT mashup-", "version": 3}, + "347": {"name": "電車で電車でOPA!OPA!OPA! -GMT mashup-", "version": 3}, + "348": {"name": "Axeria", "version": 5}, + "349": {"name": "ピーマンたべたら", "version": 4}, + "351": {"name": "MIRROR of MAGIC", "version": 4}, + "352": {"name": "究極焼肉レストラン!お燐の地獄亭!", "version": 4}, + "353": {"name": "shake it!", "version": 4}, + "356": {"name": "1/3の純情な感情", "version": 5}, + "357": {"name": "みかんのうた", "version": 4}, + "358": {"name": "毒占欲", "version": 4}, + "359": {"name": "YU-MU", "version": 6}, + "360": {"name": "物凄い勢いでけーねが物凄いうた", "version": 6}, + "362": {"name": "BETTER CHOICE", "version": 4}, + "363": {"name": "Oshama Scramble!", "version": 5}, + "364": {"name": "D✪N’T ST✪P R✪CKIN’", "version": 5}, + "365": {"name": "ガラテアの螺旋", "version": 5}, + "366": {"name": "oboro", "version": 4}, + "367": {"name": "Dragoon", "version": 4}, + "368": {"name": "シリウス", "version": 5}, + "369": {"name": "ほとんど違法行為", "version": 4}, + "370": {"name": "naraku within", "version": 4}, + "371": {"name": "Stand Up!!!!", "version": 4}, + "372": {"name": "ファミリーパーティー", "version": 4}, + "374": {"name": "アンハッピーリフレイン", "version": 4}, + "375": {"name": "One Step Ahead", "version": 4}, + "376": {"name": "L9", "version": 5}, + "377": {"name": "おいでよ!妖精の森", "version": 4}, + "378": {"name": "planet dancer", "version": 4}, + "379": {"name": "Caliburne ~Story of the Legendary sword~", "version": 4}, + "380": {"name": "どうしてこうなった", "version": 5}, + "381": {"name": "B.B.K.K.B.K.K.", "version": 4}, + "382": {"name": "おこちゃま戦争", "version": 4}, + "383": {"name": "Link", "version": 4}, + "384": {"name": "VERTeX", "version": 4}, + "385": {"name": "Ignis Danse", "version": 4}, + "386": {"name": "Scars of FAUNA", "version": 4}, + "387": {"name": "FUJIN Rumble", "version": 4}, + "388": {"name": "きたさいたま2000", "version": 4}, + "389": {"name": "FLOWER", "version": 4}, + "390": {"name": "Got more raves?", "version": 4}, + "397": {"name": "Can Do", "version": 5}, + "398": {"name": "The Other self", "version": 5}, + "399": {"name": "不毛!", "version": 4}, + "400": {"name": "デッドレッドガールズ", "version": 5}, + "401": {"name": "赤心性:カマトト荒療治", "version": 5}, + "402": {"name": "ニブンノイチ", "version": 5}, + "403": {"name": "悪戯", "version": 5}, + "404": {"name": "響縁", "version": 5}, + "405": {"name": "カノン", "version": 5}, + "406": {"name": "オルフェ", "version": 5}, + "407": {"name": "LOL -lots of laugh-", "version": 6}, + "408": {"name": "クローバー♣クラブ", "version": 5}, + "409": {"name": "妄想税", "version": 5}, + "410": {"name": "どういうことなの!?", "version": 5}, + "411": {"name": "初音ミクの消失", "version": 6}, + "412": {"name": "イノコリ先生", "version": 6}, + "413": {"name": "キャプテン・ムラサのケツアンカー", "version": 5}, + "414": {"name": "若い力 -SEGA HARD GIRLS MIX-", "version": 5}, + "416": {"name": "ココロオドル", "version": 5}, + "417": {"name": "ウミユリ海底譚", "version": 5}, + "418": {"name": "welcome to maimai!! with マイマイマー", "version": 5}, + "419": {"name": "ストリーミングハート", "version": 5}, + "420": {"name": "Change Our MIRAI!", "version": 5}, + "421": {"name": "Aiolos", "version": 5}, + "422": {"name": "LANCE", "version": 5}, + "423": {"name": "SAVIOR OF SONG", "version": 5}, + "424": {"name": "橙の幻想郷音頭", "version": 6}, + "425": {"name": "東方妖々夢 ~the maximum moving about~", "version": 6}, + "426": {"name": "蒼空に舞え、墨染の桜", "version": 6}, + "427": {"name": "少女幻葬戦慄曲 ~ Necro Fantasia", "version": 6}, + "431": {"name": "Contrapasso -paradiso-", "version": 7}, + "432": {"name": "幸せになれる隠しコマンドがあるらしい", "version": 9}, + "434": {"name": "フラグメンツ -T.V. maimai edit-", "version": 6}, + "435": {"name": "願いを呼ぶ季節", "version": 6}, + "436": {"name": "おじゃま虫", "version": 5}, + "437": {"name": "Yet Another ”drizzly rain”", "version": 5}, + "438": {"name": "四次元跳躍機関", "version": 5}, + "439": {"name": "最終鬼畜妹・一部声", "version": 5}, + "440": {"name": "はじめまして地球人さん", "version": 5}, + "441": {"name": "Pursuing My True Self", "version": 5}, + "442": {"name": "Signs Of Love (“Never More” ver.)", "version": 5}, + "443": {"name": "specialist (“Never More” ver.)", "version": 5}, + "444": {"name": "Time To Make History (AKIRA YAMAOKA Remix)", "version": 5}, + "446": {"name": "GEMINI -M-", "version": 6}, + "447": {"name": "+♂", "version": 6}, + "448": {"name": "ありふれたせかいせいふく", "version": 6}, + "449": {"name": "すろぉもぉしょん", "version": 6}, + "450": {"name": "絵の上手かった友達", "version": 6}, + "451": {"name": "頓珍漢の宴", "version": 6}, + "453": {"name": "Garden Of The Dragon", "version": 6}, + "454": {"name": "After Burner", "version": 6}, + "455": {"name": "Counselor", "version": 6}, + "456": {"name": "Glorious Crown", "version": 6}, + "457": {"name": "言ノ葉遊戯", "version": 6}, + "458": {"name": "りばーぶ", "version": 6}, + "459": {"name": "幾四音-Ixion-", "version": 6}, + "460": {"name": "閃鋼のブリューナク", "version": 6}, + "461": {"name": "無敵We are one!!", "version": 7}, + "462": {"name": "7thSense", "version": 6}, + "463": {"name": "FEEL the BEATS", "version": 6}, + "464": {"name": "Revive The Rave", "version": 6}, + "465": {"name": "スリップフリップ", "version": 6}, + "466": {"name": "Jimang Shot", "version": 6}, + "467": {"name": "儚きもの人間", "version": 7}, + "468": {"name": "ラブリー☆えんじぇる!!", "version": 6}, + "469": {"name": "ひみつをちょーだい", "version": 6}, + "470": {"name": "Touch Tap Baby", "version": 6}, + "471": {"name": "オモイヨシノ", "version": 6}, + "472": {"name": "アージェントシンメトリー", "version": 6}, + "473": {"name": "ゲラゲラポーのうた", "version": 6}, + "474": {"name": "秘密の扉から会いにきて", "version": 6}, + "475": {"name": "かくしん的☆めたまるふぉ~ぜっ!", "version": 6}, + "476": {"name": "極上スマイル", "version": 6}, + "477": {"name": "厨病激発ボーイ", "version": 6}, + "479": {"name": "Dragon Night", "version": 6}, + "480": {"name": "HIMITSUスパーク", "version": 6}, + "481": {"name": "でんでんぱっしょん", "version": 6}, + "482": {"name": "Party 4U ”holy nite mix”", "version": 6}, + "483": {"name": "囲い無き世は一期の月影", "version": 6}, + "484": {"name": "Luminize", "version": 6}, + "485": {"name": "Falling Roses", "version": 6}, + "486": {"name": "青春はNon-Stop!", "version": 6}, + "487": {"name": "ようかい体操第一", "version": 7}, + "488": {"name": "Lividi", "version": 8}, + "490": {"name": "Infantoon Fantasy", "version": 7}, + "491": {"name": "ヘタリアン☆ジェット", "version": 7}, + "492": {"name": "Jumble Rumble", "version": 7}, + "493": {"name": "Imitation:Loud Lounge", "version": 8}, + "494": {"name": "CITRUS MONSTER", "version": 7}, + "495": {"name": "Hyper Active", "version": 7}, + "496": {"name": "AMAZING MIGHTYYYY!!!!", "version": 7}, + "497": {"name": "fake!fake!", "version": 7}, + "498": {"name": "夏にキスしていいですか?", "version": 7}, + "499": {"name": "夏祭り", "version": 7}, + "500": {"name": "シュガーソングとビターステップ", "version": 7}, + "501": {"name": "Daydream café", "version": 7}, + "502": {"name": "Scatman (Ski Ba Bop Ba Dop Bop)", "version": 7}, + "503": {"name": "イチズレシピ", "version": 7}, + "504": {"name": "ふ・れ・ん・ど・し・た・い", "version": 7}, + "505": {"name": "ちょちょちょ!ゆるゆり☆かぷりっちょ!!!", "version": 8}, + "506": {"name": "セハガガガンバッちゃう!!", "version": 7}, + "507": {"name": "キミノヨゾラ哨戒班", "version": 7}, + "508": {"name": "恋愛裁判", "version": 7}, + "509": {"name": "Just Be Friends", "version": 8}, + "510": {"name": "ヒビカセ", "version": 11}, + "511": {"name": "パーフェクト生命", "version": 7}, + "512": {"name": "StargazeR", "version": 8}, + "513": {"name": "だんだん早くなる", "version": 8}, + "514": {"name": "ヘルシーエンド", "version": 9}, + "515": {"name": "クノイチでも恋がしたい", "version": 7}, + "516": {"name": "ココロ", "version": 8}, + "517": {"name": "東京リアルワールド", "version": 7}, + "518": {"name": "やめろ!聴くな!", "version": 7}, + "519": {"name": "ゆっくりしていってね!!!", "version": 8}, + "520": {"name": "木彫り鯰と右肩ゾンビ", "version": 7}, + "521": {"name": "ECHO", "version": 7}, + "522": {"name": "Mr. Wonderland", "version": 7}, + "523": {"name": "しんでしまうとはなさけない!", "version": 7}, + "524": {"name": "Hand in Hand", "version": 7}, + "525": {"name": "ブリキノダンス", "version": 11}, + "526": {"name": "brilliant better", "version": 7}, + "527": {"name": "ハート・ビート", "version": 7}, + "528": {"name": "Invitation", "version": 7}, + "529": {"name": "connecting with you", "version": 7}, + "530": {"name": "高気圧ねこロック", "version": 7}, + "531": {"name": "Prophesy One", "version": 7}, + "532": {"name": "洗脳", "version": 7}, + "533": {"name": "Barbed Eye", "version": 7}, + "534": {"name": "Nitrous Fury", "version": 7}, + "535": {"name": "色は匂へど散りぬるを", "version": 8}, + "536": {"name": "月に叢雲華に風", "version": 7}, + "537": {"name": "No Routine", "version": 7}, + "538": {"name": "ひれ伏せ愚民どもっ!", "version": 7}, + "539": {"name": "エテルニタス・ルドロジー", "version": 7}, + "540": {"name": "泡沫、哀のまほろば", "version": 10}, + "541": {"name": "華鳥風月", "version": 9}, + "542": {"name": "METATRON", "version": 7}, + "543": {"name": "終わりなき物語", "version": 7}, + "544": {"name": "ゆりゆららららゆるゆり大事件", "version": 8}, + "546": {"name": "フォルテシモBELL", "version": 8}, + "547": {"name": "私の中の幻想的世界観及びその顕現を想起させたある現実での出来事に関する一考察", "version": 8}, + "548": {"name": "DETARAME ROCK&ROLL THEORY", "version": 8}, + "550": {"name": "PERFECT HUMAN", "version": 8}, + "551": {"name": "ファッとして桃源郷", "version": 8}, + "552": {"name": "ゴーストルール", "version": 8}, + "553": {"name": "チュルリラ・チュルリラ・ダッダッダ!", "version": 8}, + "555": {"name": "GOODMEN", "version": 8}, + "556": {"name": "夜明けまであと3秒", "version": 8}, + "557": {"name": "Last Brave ~ Go to zero", "version": 8}, + "558": {"name": "STAIRWAY TO GENERATION", "version": 8}, + "559": {"name": "空威張りビヘイビア", "version": 8}, + "560": {"name": "回レ!雪月花", "version": 8}, + "561": {"name": "いっしそう電☆舞舞神拳!", "version": 8}, + "562": {"name": "いぇす!ゆゆゆ☆ゆるゆり♪♪", "version": 8}, + "564": {"name": "ENJOY POLIS", "version": 12}, + "565": {"name": "生きてるおばけは生きている", "version": 8}, + "566": {"name": "天火明命", "version": 8}, + "567": {"name": "HERA", "version": 8}, + "568": {"name": "分からない", "version": 8}, + "571": {"name": "Our Wrenally", "version": 8}, + "572": {"name": "Scream out! -maimai SONIC WASHER Edit-", "version": 8}, + "573": {"name": "四月の雨", "version": 8}, + "574": {"name": "Selector", "version": 8}, + "575": {"name": "Star☆Glitter", "version": 8}, + "576": {"name": "H-A-J-I-M-A-R-I-U-T-A-!!", "version": 8}, + "578": {"name": "ドキドキDREAM!!!", "version": 8}, + "579": {"name": "ポップミュージックは僕のもの", "version": 8}, + "580": {"name": "吉原ラメント", "version": 8}, + "581": {"name": "ねぇ、壊れタ人形ハ何処へ棄テらレるノ?", "version": 8}, + "582": {"name": "幽闇に目醒めしは", "version": 8}, + "583": {"name": "Starlight Vision", "version": 8}, + "584": {"name": "Club Ibuki in Break All", "version": 8}, + "585": {"name": "Phantasm Brigade", "version": 8}, + "586": {"name": "永遠のメロディ", "version": 10}, + "587": {"name": "白ゆき", "version": 9}, + "589": {"name": "Panopticon", "version": 8}, + "592": {"name": "Sakura Fubuki", "version": 8}, + "593": {"name": "conflict", "version": 8}, + "598": {"name": "すーぱーぬこになりたい", "version": 8}, + "599": {"name": "踊れオーケストラ", "version": 8}, + "600": {"name": "SUSHI食べたい feat.ソイソース", "version": 8}, + "601": {"name": "きらっせ☆ウッド村ファーム", "version": 8}, + "602": {"name": "taboo tears you up", "version": 8}, + "603": {"name": "その群青が愛しかったようだった", "version": 8}, + "604": {"name": "Jumping!!", "version": 8}, + "605": {"name": "7 Girls War", "version": 8}, + "606": {"name": "The wheel to the right", "version": 8}, + "609": {"name": "SAKURAスキップ", "version": 9}, + "610": {"name": "リンカーネイション", "version": 9}, + "611": {"name": "宿題が終わらないっ!", "version": 9}, + "612": {"name": "GO BACK 2 YOUR RAVE", "version": 9}, + "613": {"name": "天国と地獄 -言ノ葉リンネ-", "version": 9}, + "614": {"name": "夢花火", "version": 9}, + "615": {"name": "Paradisus-Paradoxum", "version": 9}, + "617": {"name": "オトヒメモリー☆ウタゲーション", "version": 9}, + "618": {"name": "ラブチーノ", "version": 9}, + "620": {"name": "東京レトロ", "version": 9}, + "621": {"name": "ARROW", "version": 9}, + "622": {"name": "バッド・ダンス・ホール", "version": 9}, + "624": {"name": "KISS CANDY FLAVOR", "version": 9}, + "625": {"name": "Maxi", "version": 9}, + "626": {"name": "相思創愛", "version": 9}, + "627": {"name": "心象蜃気楼", "version": 9}, + "628": {"name": "光線チューニング", "version": 9}, + "629": {"name": "Limit Break", "version": 9}, + "630": {"name": "曖昧mind", "version": 9}, + "631": {"name": "KING is BACK!!", "version": 9}, + "632": {"name": "Ultranova", "version": 9}, + "634": {"name": "GET!! 夢&DREAM", "version": 9}, + "636": {"name": "日本の米は世界一", "version": 9}, + "637": {"name": "ないせんのうた", "version": 9}, + "639": {"name": "Starlight Dance Floor", "version": 9}, + "640": {"name": "妖精村の月誕祭 ~Lunate Elf", "version": 9}, + "641": {"name": "Calamity Fortune", "version": 9}, + "642": {"name": "Justified", "version": 10}, + "643": {"name": "Excalibur ~Revived resolution~", "version": 10}, + "645": {"name": "ホシトハナ", "version": 9}, + "646": {"name": "名探偵連続殺人事件", "version": 9}, + "647": {"name": "エイリアンエイリアン", "version": 9}, + "648": {"name": "My Dearest Song", "version": 9}, + "649": {"name": "猛進ソリストライフ!", "version": 9}, + "650": {"name": "Let's Go Away", "version": 11}, + "653": {"name": "Now Loading!!!!", "version": 9}, + "654": {"name": "真・ハンサム体操でズンドコホイ", "version": 9}, + "655": {"name": "フラジール", "version": 9}, + "656": {"name": "未来(ソラ)の歌", "version": 9}, + "657": {"name": "オーディエンスを沸かす程度の能力 feat.タイツォン", "version": 9}, + "658": {"name": "人里に下ったアタイがいつの間にか社畜になっていた件", "version": 9}, + "659": {"name": "ギリギリ最強あいまいみー!", "version": 10}, + "661": {"name": "CALL HEAVEN!!", "version": 9}, + "662": {"name": "Sunshine world tour", "version": 9}, + "663": {"name": "ちがう!!!", "version": 9}, + "664": {"name": "Moon of Noon", "version": 9}, + "665": {"name": "チルノのパーフェクトさんすう教室 ⑨周年バージョン", "version": 9}, + "666": {"name": "デスパレイト", "version": 9}, + "668": {"name": "ようこそジャパリパークへ", "version": 10}, + "669": {"name": "あ・え・い・う・え・お・あお!!", "version": 10}, + "670": {"name": "ドーナツホール", "version": 10}, + "672": {"name": "MilK", "version": 10}, + "673": {"name": "咲キ誇レ常世ノ華", "version": 10}, + "674": {"name": "Magical Flavor", "version": 10}, + "675": {"name": "白い雪のプリンセスは", "version": 10}, + "676": {"name": "好き!雪!本気マジック", "version": 10}, + "677": {"name": "スターナイトスノウ", "version": 10}, + "678": {"name": "This game", "version": 10}, + "679": {"name": "Los! Los! Los!", "version": 10}, + "680": {"name": "Candy Tall Woman", "version": 10}, + "681": {"name": "Signature", "version": 10}, + "682": {"name": "人生リセットボタン", "version": 10}, + "683": {"name": "しねばいいのに", "version": 10}, + "684": {"name": "もうみんなしねばいいのに", "version": 10}, + "687": {"name": "共感覚おばけ", "version": 10}, + "688": {"name": "麒麟", "version": 10}, + "689": {"name": "Credits", "version": 10}, + "690": {"name": "ダンスロボットダンス", "version": 10}, + "691": {"name": "アルカリレットウセイ", "version": 10}, + "692": {"name": "ドクハク", "version": 10}, + "693": {"name": "キレキャリオン", "version": 10}, + "694": {"name": "猫祭り", "version": 10}, + "695": {"name": "TRUST", "version": 10}, + "696": {"name": "Still", "version": 10}, + "697": {"name": "ガヴリールドロップキック", "version": 10}, + "698": {"name": "fantastic dreamer", "version": 10}, + "699": {"name": "天狗の落とし文 feat. ytr", "version": 10}, + "700": {"name": "疾走あんさんぶる", "version": 10}, + "701": {"name": "Doll Judgment", "version": 10}, + "702": {"name": "WARNING×WARNING×WARNING", "version": 10}, + "704": {"name": "SPILL OVER COLORS", "version": 10}, + "705": {"name": "Mare Maris", "version": 10}, + "706": {"name": "シャルル", "version": 10}, + "707": {"name": "フリィダム ロリィタ", "version": 10}, + "708": {"name": "バレリーコ", "version": 10}, + "709": {"name": "アウターサイエンス", "version": 10}, + "710": {"name": "REVIVER オルタンシア・サーガ -蒼の騎士団- オリジナルVer.", "version": 10}, + "711": {"name": "拝啓ドッペルゲンガー", "version": 10}, + "712": {"name": "ミラクル・ショッピング", "version": 10}, + "713": {"name": "Kinda Way", "version": 10}, + "717": {"name": "ラブって♡ジュエリー♪えんじぇる☆ブレイク!!", "version": 12}, + "718": {"name": "ヤバい○○", "version": 10}, + "719": {"name": "エンドマークに希望と涙を添えて", "version": 10}, + "720": {"name": "ナンセンス文学", "version": 10}, + "721": {"name": "Bang Babang Bang!!!", "version": 10}, + "722": {"name": "Tic Tac DREAMIN’", "version": 10}, + "723": {"name": "SPICY SWINGY STYLE", "version": 10}, + "725": {"name": "砂の惑星 feat. HATSUNE MIKU", "version": 10}, + "726": {"name": "アンノウン・マザーグース", "version": 10}, + "728": {"name": "POP TEAM EPIC", "version": 11}, + "729": {"name": "にめんせい☆ウラオモテライフ!", "version": 11}, + "730": {"name": "うまるん体操", "version": 11}, + "731": {"name": "妄想感傷代償連盟", "version": 11}, + "732": {"name": "LOVE EAST", "version": 11}, + "733": {"name": "Fist Bump", "version": 11}, + "734": {"name": "ENERGY SYNERGY MATRIX", "version": 11}, + "735": {"name": "Session High⤴", "version": 11}, + "736": {"name": "World Vanquisher", "version": 11}, + "737": {"name": "FestivaLight", "version": 11}, + "738": {"name": "Brain Power", "version": 11}, + "739": {"name": "進め!イッスン軍団 -Rebellion of the Dwarfs-", "version": 11}, + "740": {"name": "ULTRA B+K", "version": 11}, + "741": {"name": "インビジブル", "version": 11}, + "742": {"name": "彗星ハネムーン", "version": 11}, + "743": {"name": "アンチクロックワイズ", "version": 11}, + "744": {"name": "全力☆Summer!", "version": 11}, + "745": {"name": "keep hopping", "version": 11}, + "746": {"name": "larva", "version": 11}, + "747": {"name": "えれくとりっく・えんじぇぅ", "version": 11}, + "748": {"name": "ミラクルペイント", "version": 11}, + "749": {"name": "Ievan Polkka", "version": 11}, + "750": {"name": "初音ミクの激唱", "version": 11}, + "751": {"name": "ガチャガチャきゅ~と・ふぃぎゅ@メイト", "version": 12}, + "752": {"name": "true my heart -Lovable mix-", "version": 12}, + "753": {"name": "Rodeo Machine", "version": 11}, + "754": {"name": "Arrival of Tears", "version": 11}, + "756": {"name": "CYBER Sparks", "version": 11}, + "757": {"name": "Xevel", "version": 11}, + "758": {"name": "INFINITE WORLD", "version": 11}, + "759": {"name": "サドマミホリック", "version": 11}, + "760": {"name": "Ragnarok", "version": 11}, + "761": {"name": "このふざけた素晴らしき世界は、僕の為にある", "version": 11}, + "762": {"name": "このピアノでお前を8759632145回ぶん殴る", "version": 11}, + "763": {"name": "はやくそれになりたい!", "version": 11}, + "764": {"name": "フィクサー", "version": 11}, + "765": {"name": "みんなの", "version": 11}, + "766": {"name": "アマノジャクリバース feat. ytr", "version": 11}, + "767": {"name": "進捗どうですか?", "version": 11}, + "768": {"name": "Help me, あーりん!", "version": 11}, + "769": {"name": "なるとなぎのパーフェクトロックンロール教室", "version": 11}, + "770": {"name": "あねぺったん", "version": 11}, + "771": {"name": "We Gonna Journey", "version": 11}, + "772": {"name": "FREEDOM DiVE (tpz Overcute Remix)", "version": 11}, + "773": {"name": "SILENT BLUE", "version": 11}, + "774": {"name": "甲賀忍法帖", "version": 11}, + "775": {"name": "ジンギスカン", "version": 11}, + "776": {"name": "にじよめちゃん体操第一億", "version": 11}, + "777": {"name": "花と、雪と、ドラムンベース。", "version": 11}, + "778": {"name": "Conquista Ciela", "version": 11}, + "779": {"name": "怒槌", "version": 11}, + "781": {"name": "Seyana. ~何でも言うことを聞いてくれるアカネチャン~", "version": 11}, + "782": {"name": "おねがいダーリン", "version": 11}, + "786": {"name": "My First Phone", "version": 11}, + "787": {"name": "SHINY DAYS", "version": 12}, + "788": {"name": "NOISY LOVE POWER☆", "version": 12}, + "789": {"name": "ネ!コ!", "version": 12}, + "790": {"name": "only my railgun", "version": 12}, + "791": {"name": "ロールプレイングゲーム", "version": 12}, + "792": {"name": "ヒバナ", "version": 12}, + "793": {"name": "ロキ", "version": 12}, + "794": {"name": "Money Money", "version": 12}, + "796": {"name": "極圏", "version": 12}, + "797": {"name": "Scarlet Lance", "version": 12}, + "798": {"name": "セイクリッド ルイン", "version": 12}, + "799": {"name": "QZKago Requiem", "version": 12}, + "800": {"name": "EVERGREEN", "version": 12}, + "801": {"name": "金の聖夜霜雪に朽ちて", "version": 12}, + "802": {"name": "不思議の国のクリスマス", "version": 12}, + "803": {"name": "Schwarzschild", "version": 12}, + "804": {"name": "Magical Sound Shower", "version": 12}, + "805": {"name": "片想いサンバ", "version": 12}, + "806": {"name": "ナイトメア☆パーティーナイト", "version": 12}, + "807": {"name": "フキゲンワルツ", "version": 12}, + "809": {"name": "結ンデ開イテ羅刹ト骸", "version": 12}, + "810": {"name": "サウンド", "version": 12}, + "811": {"name": "右肩の蝶", "version": 12}, + "812": {"name": "Alea jacta est!", "version": 12}, + "815": {"name": "L4TS:2018 (feat. あひる & KTA)", "version": 12}, + "816": {"name": "クレイジークレイジーダンサーズ", "version": 12}, + "817": {"name": "【東方ニコカラ】秘神マターラ feat.魂音泉【IOSYS】", "version": 12}, + "818": {"name": "隠然", "version": 12}, + "819": {"name": "White Traveling Girl", "version": 12}, + "820": {"name": "FFT", "version": 12}, + "821": {"name": "-OutsideR:RequieM-", "version": 12}, + "822": {"name": "イロトリドリのメロディ", "version": 12}, + "823": {"name": "奏者はただ背中と提琴で語るのみ", "version": 12}, + "824": {"name": "Deep in Abyss", "version": 12}, + "825": {"name": "雷切-RAIKIRI-", "version": 12}, + "826": {"name": "キミとボクのミライ", "version": 12}, + "827": {"name": "Lost Princess", "version": 12}, + "828": {"name": "アリサのテーマ", "version": 12}, + "829": {"name": "敗北の少年", "version": 12}, + "830": {"name": "立ち入り禁止", "version": 12}, + "831": {"name": "お気に召すまま", "version": 12}, + "832": {"name": "命ばっかり", "version": 12}, + "833": {"name": "the EmpErroR", "version": 12}, + "834": {"name": "PANDORA PARADOXXX", "version": 12}, + "835": {"name": "Believe the Rainbow", "version": 12}, + "836": {"name": "Good Bye, Mr. Jack", "version": 12}, + "837": {"name": "Altale", "version": 12}, + "838": {"name": "最終鬼畜妹フランドール・S", "version": 12}, + "839": {"name": "ネクロファンタジア~Arr.Demetori", "version": 12}, + "840": {"name": "Imperishable Night 2006 (2016 Refine)", "version": 12}, + "841": {"name": "終点", "version": 12}, + "842": {"name": "WORLD'S END UMBRELLA", "version": 12}, + "843": {"name": "ワンダーラスト", "version": 12}, + "844": {"name": "End Time", "version": 12}, + "847": {"name": "カラフル×メロディ", "version": 12}, + "848": {"name": "魔法少女になるしかねぇ", "version": 12}, + "849": {"name": "Kattobi KEIKYU Rider", "version": 12}, + "850": {"name": "B.M.S.", "version": 12}, + "852": {"name": "TiamaT:F minor", "version": 12}, + "853": {"name": "前前前世", "version": 9}, + "854": {"name": "全世界共通リズム感テスト", "version": 15}, + "902": {"name": "次の方お願いします", "version": 0}, + "903": {"name": "カービン", "version": 0}, + "904": {"name": "とても遠い愛", "version": 0}, + "906": {"name": "So long", "version": 0}, + "907": {"name": "飛行履歴", "version": 0}, + "1020": {"name": "Technicians High", "version": 17}, + "1051": {"name": "Destr0yer", "version": 17}, + "1081": {"name": "サンバランド", "version": 18}, + "1085": {"name": "Halcyon", "version": 18}, + "1103": {"name": "渦状銀河のシンフォニエッタ", "version": 21}, + "1235": {"name": "VIIIbit Explorer", "version": 19}, + "1301": {"name": "華の集落、秋のお届け", "version": 22}, + "10021": {"name": "コネクト", "version": 14}, + "10044": {"name": "ハッピーシンセサイザ", "version": 22}, + "10070": {"name": "ジングルベル", "version": 20}, + "10117": {"name": "炎歌 -ほむらうた-", "version": 22}, + "10125": {"name": "千本桜", "version": 13}, + "10128": {"name": "御旗のもとに", "version": 22}, + "10145": {"name": "Starlight Disco", "version": 18}, + "10146": {"name": "39", "version": 13}, + "10181": {"name": "君の知らない物語", "version": 14}, + "10185": {"name": "*ハロー、プラネット。", "version": 21}, + "10188": {"name": "天ノ弱", "version": 15}, + "10190": {"name": "モザイクロール", "version": 19}, + "10191": {"name": "脳漿炸裂ガール", "version": 13}, + "10193": {"name": "セツナトリップ", "version": 18}, + "10202": {"name": "Grip & Break down !!", "version": 18}, + "10204": {"name": "ナイト・オブ・ナイツ", "version": 20}, + "10225": {"name": "sweet little sister", "version": 22}, + "10235": {"name": "火炎地獄", "version": 19}, + "10242": {"name": "ナミダと流星", "version": 21}, + "10251": {"name": "Monochrome Rainbow", "version": 20}, + "10256": {"name": "いーあるふぁんくらぶ", "version": 13}, + "10288": {"name": "六兆年と一夜物語", "version": 13}, + "10301": {"name": "患部で止まってすぐ溶ける~狂気の優曇華院", "version": 13}, + "10302": {"name": "ケロ⑨destiny", "version": 19}, + "10315": {"name": "深海少女", "version": 20}, + "10316": {"name": "M.S.S.Planet", "version": 19}, + "10319": {"name": "幻想のサテライト", "version": 13}, + "10363": {"name": "Oshama Scramble!", "version": 13}, + "10366": {"name": "oboro", "version": 21}, + "10375": {"name": "One Step Ahead", "version": 23}, + "10404": {"name": "響縁", "version": 19}, + "10411": {"name": "初音ミクの消失", "version": 21}, + "10420": {"name": "Change Our MIRAI!", "version": 13}, + "10475": {"name": "かくしん的☆めたまるふぉ~ぜっ!", "version": 13}, + "10500": {"name": "シュガーソングとビターステップ", "version": 13}, + "10501": {"name": "Daydream café", "version": 15}, + "10535": {"name": "色は匂へど散りぬるを", "version": 21}, + "10536": {"name": "月に叢雲華に風", "version": 13}, + "10552": {"name": "ゴーストルール", "version": 17}, + "10560": {"name": "回レ!雪月花", "version": 13}, + "10572": {"name": "Scream out! -maimai SONIC WASHER Edit-", "version": 13}, + "10574": {"name": "Selector", "version": 20}, + "10593": {"name": "conflict", "version": 13}, + "10602": {"name": "taboo tears you up", "version": 17}, + "10615": {"name": "Paradisus-Paradoxum", "version": 14}, + "10625": {"name": "Maxi", "version": 19}, + "10641": {"name": "Calamity Fortune", "version": 13}, + "10665": {"name": "チルノのパーフェクトさんすう教室 ⑨周年バージョン", "version": 13}, + "10668": {"name": "ようこそジャパリパークへ", "version": 13}, + "10690": {"name": "ダンスロボットダンス", "version": 15}, + "10702": {"name": "WARNING×WARNING×WARNING", "version": 13}, + "10706": {"name": "シャルル", "version": 13}, + "10728": {"name": "POP TEAM EPIC", "version": 13}, + "10734": {"name": "ENERGY SYNERGY MATRIX", "version": 13}, + "11001": {"name": "BLACK ROSE", "version": 13}, + "11002": {"name": "Secret Sleuth", "version": 13}, + "11003": {"name": "でらっくmaimai♪てんてこまい!", "version": 13}, + "11004": {"name": "MAXRAGE", "version": 13}, + "11005": {"name": "バーチャルダム ネーション", "version": 13}, + "11006": {"name": "P-qoq", "version": 13}, + "11007": {"name": "超常マイマイン", "version": 13}, + "11008": {"name": "Crazy Circle", "version": 13}, + "11009": {"name": "STEREOSCAPE", "version": 13}, + "11010": {"name": "STARTLINER", "version": 13}, + "11014": {"name": "♡マイマイマイラブ♡", "version": 13}, + "11015": {"name": "一か罰", "version": 13}, + "11016": {"name": "キリキリ舞Mine", "version": 13}, + "11017": {"name": "福宿音屋魂音泉", "version": 13}, + "11018": {"name": "Now or Never", "version": 13}, + "11019": {"name": "Scarlet Wings", "version": 13}, + "11020": {"name": "Technicians High", "version": 13}, + "11021": {"name": "魔ジョ狩リ", "version": 13}, + "11022": {"name": "TwisteD! XD", "version": 13}, + "11023": {"name": "Blows Up Everything", "version": 13}, + "11024": {"name": "Agitation!", "version": 13}, + "11025": {"name": "管弦楽組曲 第3番 ニ長調「第2曲(G線上のアリア)」BWV.1068-2", "version": 13}, + "11026": {"name": "TEmPTaTiON", "version": 13}, + "11027": {"name": "アポカリプスに反逆の焔を焚べろ", "version": 13}, + "11028": {"name": "玩具狂奏曲 -終焉-", "version": 13}, + "11029": {"name": "Titania", "version": 13}, + "11030": {"name": "BOKUTO", "version": 13}, + "11031": {"name": "立川浄穢捕物帳", "version": 13}, + "11032": {"name": "CHOCOLATE BOMB!!!!", "version": 14}, + "11034": {"name": "METEOR", "version": 13}, + "11035": {"name": "LOSER", "version": 13}, + "11036": {"name": "U.S.A.", "version": 13}, + "11037": {"name": "HOT LIMIT", "version": 13}, + "11038": {"name": "新宝島", "version": 13}, + "11039": {"name": "UNION", "version": 13}, + "11041": {"name": "乗ってけ!ジャパリビート", "version": 13}, + "11042": {"name": "フレ!フレ!ベストフレンズ", "version": 13}, + "11043": {"name": "メルト", "version": 13}, + "11044": {"name": "メルティランドナイトメア", "version": 13}, + "11045": {"name": "アウトサイダー", "version": 13}, + "11046": {"name": "アディショナルメモリー", "version": 13}, + "11047": {"name": "ジャガーノート", "version": 13}, + "11048": {"name": "ナイト・オブ・ナイツ (Cranky Remix)", "version": 13}, + "11049": {"name": "Bad Apple!! feat.nomico (REDALiCE Remix)", "version": 14}, + "11050": {"name": "Strobe♡Girl", "version": 13}, + "11051": {"name": "Destr0yer", "version": 13}, + "11052": {"name": "グリーンライツ・セレナーデ", "version": 13}, + "11055": {"name": "POPPY PAPPY DAY", "version": 13}, + "11056": {"name": "Clattanoia", "version": 13}, + "11057": {"name": "The Gong of Knockout", "version": 13}, + "11058": {"name": "デンパラダイム", "version": 13}, + "11059": {"name": "骸骨楽団とリリア", "version": 13}, + "11060": {"name": "星屑ユートピア", "version": 13}, + "11061": {"name": "アマツキツネ", "version": 13}, + "11062": {"name": "徒花ネクロマンシー", "version": 13}, + "11063": {"name": "目覚めRETURNER", "version": 13}, + "11064": {"name": "Little \"Sister\" Bitch", "version": 13}, + "11065": {"name": "Yakumo >>JOINT STRUGGLE (2019 Update)", "version": 13}, + "11066": {"name": "げきオコスティックファイナリアリティぷんぷんマスタースパーク", "version": 13}, + "11067": {"name": "CocktaiL", "version": 13}, + "11069": {"name": "太陽系デスコ", "version": 13}, + "11070": {"name": "だからパンを焼いたんだ", "version": 13}, + "11071": {"name": "東奔西走行進曲", "version": 13}, + "11072": {"name": "Soul-ride ON!", "version": 13}, + "11073": {"name": "アカリがやってきたぞっ", "version": 13}, + "11075": {"name": "プナイプナイせんそう", "version": 13}, + "11076": {"name": "未完成人", "version": 13}, + "11077": {"name": "デビル☆アイドル", "version": 13}, + "11078": {"name": "quiet room", "version": 13}, + "11080": {"name": "Jump!! Jump!! Jump!!", "version": 13}, + "11081": {"name": "サンバランド", "version": 13}, + "11083": {"name": "サヨナラチェーンソー", "version": 13}, + "11084": {"name": "イカサマライフゲイム", "version": 13}, + "11085": {"name": "Halcyon", "version": 13}, + "11086": {"name": "だれかの心臓になれたなら", "version": 13}, + "11087": {"name": "幾望の月", "version": 13}, + "11088": {"name": "39みゅーじっく!", "version": 13}, + "11089": {"name": "STEEL TRANSONIC", "version": 14}, + "11090": {"name": "Flashkick", "version": 14}, + "11091": {"name": "Stardust Memories", "version": 14}, + "11092": {"name": "My My My", "version": 14}, + "11093": {"name": "UniTas", "version": 14}, + "11094": {"name": "ここからはじまるプロローグ。", "version": 14}, + "11095": {"name": "絡めトリック利己ライザー", "version": 14}, + "11096": {"name": "モ°ルモ°ル", "version": 14}, + "11097": {"name": "ブレインジャックシンドローム", "version": 14}, + "11098": {"name": "共鳴", "version": 14}, + "11099": {"name": "Ututu", "version": 14}, + "11100": {"name": "シエルブルーマルシェ", "version": 14}, + "11101": {"name": "GRÄNDIR", "version": 14}, + "11102": {"name": "封焔の135秒", "version": 14}, + "11103": {"name": "渦状銀河のシンフォニエッタ", "version": 14}, + "11104": {"name": "ヤミツキ", "version": 14}, + "11105": {"name": "ワードワードワード", "version": 14}, + "11106": {"name": "Valsqotch", "version": 14}, + "11107": {"name": "最強 the サマータイム!!!!!", "version": 14}, + "11108": {"name": "UTAKATA", "version": 14}, + "11109": {"name": "タテマエと本心の大乱闘", "version": 14}, + "11110": {"name": "はちみつアドベンチャー", "version": 14}, + "11111": {"name": "popcorn", "version": 14}, + "11113": {"name": "ハム太郎とっとこうた", "version": 14}, + "11114": {"name": "青空のラプソディ", "version": 14}, + "11115": {"name": "だから僕は音楽を辞めた", "version": 14}, + "11116": {"name": "ラブ・ドラマティック feat. 伊原六花", "version": 14}, + "11117": {"name": "異世界かるてっと", "version": 14}, + "11118": {"name": "いつかいい感じにアレしよう", "version": 14}, + "11119": {"name": "I'm with you", "version": 14}, + "11120": {"name": "オトモダチフィルム", "version": 14}, + "11121": {"name": "ビターチョコデコレーション", "version": 14}, + "11122": {"name": "アンドロイドガール", "version": 14}, + "11123": {"name": "スターリースカイ☆パレード", "version": 14}, + "11124": {"name": "スロウダウナー", "version": 14}, + "11125": {"name": "クレイジー・ビート", "version": 14}, + "11126": {"name": "バイオレンストリガー", "version": 14}, + "11127": {"name": "グラーヴェ", "version": 14}, + "11128": {"name": "KILLER B", "version": 14}, + "11129": {"name": "すーぱーぬこになれんかった", "version": 14}, + "11130": {"name": "アスヘノBRAVE", "version": 14}, + "11131": {"name": "深海シティアンダーグラウンド", "version": 14}, + "11132": {"name": "表裏一体", "version": 14}, + "11133": {"name": "Catch the Wave", "version": 14}, + "11134": {"name": "ソリッド", "version": 14}, + "11135": {"name": "全力ハッピーライフ", "version": 14}, + "11136": {"name": "Oath Act", "version": 14}, + "11137": {"name": "Witches night", "version": 14}, + "11138": {"name": "Drive Your Fire", "version": 14}, + "11139": {"name": "wheel", "version": 14}, + "11140": {"name": "Black Lair", "version": 14}, + "11141": {"name": "energy trixxx", "version": 14}, + "11142": {"name": "NULCTRL", "version": 14}, + "11143": {"name": "アトロポスと最果の探究者", "version": 14}, + "11144": {"name": "六厘歌", "version": 14}, + "11145": {"name": "Entrance", "version": 14}, + "11146": {"name": "Lunar Mare", "version": 14}, + "11147": {"name": "Saika", "version": 14}, + "11148": {"name": "CHAOS", "version": 14}, + "11149": {"name": "Maboroshi", "version": 14}, + "11150": {"name": "ウマイネームイズうまみちゃん", "version": 14}, + "11151": {"name": "馬と鹿", "version": 14}, + "11152": {"name": "ぼくたちいつでも しゅわっしゅわ!", "version": 15}, + "11153": {"name": "Boys O’Clock", "version": 15}, + "11154": {"name": "居並ぶ穀物と溜息まじりの運送屋", "version": 15}, + "11155": {"name": "ARAIS", "version": 15}, + "11156": {"name": "マイオドレ!舞舞タイム", "version": 15}, + "11157": {"name": "Aetheric Energy", "version": 15}, + "11158": {"name": "Komplexe", "version": 15}, + "11159": {"name": "Beautiful Future", "version": 15}, + "11160": {"name": "Mutation", "version": 15}, + "11161": {"name": "オリフィス", "version": 15}, + "11162": {"name": "ユメヒバナ", "version": 15}, + "11163": {"name": "REAL VOICE", "version": 15}, + "11164": {"name": "パラボラ", "version": 15}, + "11165": {"name": "Regulus", "version": 15}, + "11166": {"name": "ワンダーシャッフェンの法則", "version": 15}, + "11167": {"name": "BIRTH", "version": 15}, + "11168": {"name": "シアトリカル・ケース", "version": 15}, + "11169": {"name": "ステップアンドライム", "version": 16}, + "11170": {"name": "届かない花束", "version": 16}, + "11171": {"name": "YURUSHITE", "version": 15}, + "11172": {"name": "ポケットからぬりつぶせ!", "version": 15}, + "11173": {"name": "トリドリ⇒モリモリ!Lovely fruits☆", "version": 15}, + "11174": {"name": "Desperado Waltz", "version": 15}, + "11175": {"name": "宛城、炎上!!", "version": 15}, + "11176": {"name": "Climax", "version": 15}, + "11177": {"name": "レーイレーイ", "version": 15}, + "11178": {"name": "ノーポイッ!", "version": 15}, + "11179": {"name": "町かどタンジェント", "version": 15}, + "11180": {"name": "紅蓮華", "version": 15}, + "11182": {"name": "MIRACLE RUSH", "version": 15}, + "11183": {"name": "お願いマッスル", "version": 15}, + "11184": {"name": "Virtual to LIVE", "version": 15}, + "11185": {"name": "命に嫌われている", "version": 15}, + "11186": {"name": "アンクローズ・ヒューマン", "version": 15}, + "11187": {"name": "乙女解剖", "version": 15}, + "11188": {"name": "絶え間なく藍色", "version": 15}, + "11189": {"name": "ガランド", "version": 15}, + "11190": {"name": "とりあえずアナタがいなくなるまえに", "version": 15}, + "11191": {"name": "明星ギャラクティカ", "version": 15}, + "11192": {"name": "絶対にチョコミントを食べるアオイチャン", "version": 15}, + "11193": {"name": "デリヘル呼んだら君が来た", "version": 15}, + "11194": {"name": "ベノム", "version": 15}, + "11195": {"name": "マネマネサイコトロピック", "version": 15}, + "11197": {"name": "劣等上等", "version": 19}, + "11198": {"name": "アスノヨゾラ哨戒班", "version": 15}, + "11199": {"name": "悪戯センセーション", "version": 15}, + "11200": {"name": "Paranoia", "version": 15}, + "11201": {"name": "絡繰りドール", "version": 15}, + "11202": {"name": "物凄いヴァイブスで魔理沙が物凄いラップ", "version": 15}, + "11204": {"name": "Sweet Requiem", "version": 15}, + "11205": {"name": "Dive into The Sky ~initialized~", "version": 15}, + "11206": {"name": "BATTLE NO.1", "version": 15}, + "11207": {"name": "Ether Strike", "version": 15}, + "11208": {"name": "Cyaegha", "version": 15}, + "11209": {"name": "Grievous Lady", "version": 15}, + "11210": {"name": "c.s.q.n.", "version": 15}, + "11211": {"name": "カラッポ・ノンフィクション", "version": 15}, + "11212": {"name": "emomomo", "version": 15}, + "11213": {"name": "Arty Party", "version": 15}, + "11214": {"name": "Jörqer", "version": 15}, + "11215": {"name": "Sqlupp (Camellia's \"Sqleipd*Hiytex\" Remix)", "version": 15}, + "11216": {"name": "felys -final remix-", "version": 15}, + "11217": {"name": "Pretender", "version": 15}, + "11218": {"name": "自傷無色", "version": 19}, + "11219": {"name": "Prismatic", "version": 15}, + "11221": {"name": "≠彡\"/了→", "version": 15}, + "11222": {"name": "BREaK! BREaK! BREaK!", "version": 15}, + "11223": {"name": "Heavenly Blast", "version": 16}, + "11224": {"name": "Paradisoda", "version": 16}, + "11225": {"name": "VANTABLACK RAVER", "version": 16}, + "11226": {"name": "時計の国のジェミニ", "version": 16}, + "11227": {"name": "Xenovcipher", "version": 16}, + "11228": {"name": "星めぐり、果ての君へ。", "version": 16}, + "11229": {"name": "スローアライズ", "version": 16}, + "11230": {"name": "チエルカ/エソテリカ", "version": 16}, + "11231": {"name": "生命不詳", "version": 16}, + "11232": {"name": "Never Give Up!", "version": 16}, + "11233": {"name": "Starry Colors", "version": 16}, + "11234": {"name": "ほしぞらスペクタクル", "version": 16}, + "11235": {"name": "VIIIbit Explorer", "version": 16}, + "11236": {"name": "Last Samurai", "version": 16}, + "11237": {"name": "蒼穹舞楽", "version": 16}, + "11238": {"name": "AMABIE", "version": 16}, + "11239": {"name": "オーケー? オーライ!", "version": 16}, + "11240": {"name": "サヨナラフリーウェイ", "version": 16}, + "11241": {"name": "単一指向性オーバーブルーム", "version": 16}, + "11242": {"name": "みなえをチェック!", "version": 16}, + "11243": {"name": "ハジマリノピアノ", "version": 16}, + "11246": {"name": "BOUNCE & DANCE", "version": 16}, + "11247": {"name": "Kiss Me Kiss", "version": 16}, + "11248": {"name": "MEGATON BLAST", "version": 16}, + "11249": {"name": "Splash Dance!!", "version": 16}, + "11250": {"name": "インフェルノ", "version": 16}, + "11251": {"name": "冬のこもりうた", "version": 16}, + "11252": {"name": "God knows...", "version": 16}, + "11253": {"name": "おジャ魔女カーニバル!!", "version": 16}, + "11254": {"name": "秒針を噛む", "version": 16}, + "11255": {"name": "タケモトピアノCMソング", "version": 16}, + "11256": {"name": "乙女のルートはひとつじゃない!", "version": 16}, + "11257": {"name": "ビックカメラのテーマソング", "version": 16}, + "11258": {"name": "電話革命ナイセン", "version": 16}, + "11259": {"name": "Shiny Smily Story", "version": 16}, + "11260": {"name": "ネガティブ進化論", "version": 16}, + "11261": {"name": "雨とペトラ", "version": 16}, + "11262": {"name": "永遠にゲームで対戦したいキリタン", "version": 16}, + "11263": {"name": "KING", "version": 16}, + "11264": {"name": "幽霊東京", "version": 16}, + "11265": {"name": "Alice in 冷凍庫", "version": 16}, + "11266": {"name": "キラメキ居残り大戦争", "version": 16}, + "11267": {"name": "ディカディズム", "version": 16}, + "11268": {"name": "猫猫的宇宙論", "version": 16}, + "11269": {"name": "ラットが死んだnew words", "version": 16}, + "11270": {"name": "阿吽のビーツ", "version": 16}, + "11271": {"name": "再会", "version": 16}, + "11272": {"name": "約束", "version": 16}, + "11273": {"name": "トラフィック・ジャム", "version": 16}, + "11274": {"name": "アルティメットセンパイ", "version": 16}, + "11275": {"name": "ロストワードクロニカル", "version": 16}, + "11276": {"name": "今、誰が為のかがり火へ", "version": 16}, + "11277": {"name": "Melody!", "version": 16}, + "11278": {"name": "下克上々", "version": 16}, + "11279": {"name": "ハウリング", "version": 16}, + "11280": {"name": "トランスダンスアナーキー", "version": 16}, + "11281": {"name": "Re:End of a Dream", "version": 16}, + "11282": {"name": "もぺもぺ", "version": 16}, + "11283": {"name": "Sound Chimera", "version": 16}, + "11284": {"name": "CO5M1C R4ILR0AD", "version": 16}, + "11285": {"name": "MAKE IT FUNKY NOW", "version": 16}, + "11286": {"name": "Rush-More", "version": 16}, + "11287": {"name": "Strange Bar", "version": 16}, + "11288": {"name": "Big Daddy", "version": 16}, + "11289": {"name": "ベースラインやってる?笑", "version": 16}, + "11290": {"name": "ALiVE", "version": 16}, + "11291": {"name": "ヒミツCULT", "version": 16}, + "11292": {"name": "MEGATON KICK", "version": 16}, + "11293": {"name": "LOSE CONTROL", "version": 16}, + "11294": {"name": "宿星審判", "version": 16}, + "11295": {"name": "脳天直撃", "version": 17}, + "11296": {"name": "とびだせ!TO THE COSMIC!!", "version": 17}, + "11297": {"name": "噛み係", "version": 17}, + "11298": {"name": "トリアージ", "version": 17}, + "11299": {"name": "NAGAREBOSHI☆ROCKET", "version": 17}, + "11300": {"name": "U&iVERSE -銀河鸞翔-", "version": 17}, + "11301": {"name": "華の集落、秋のお届け", "version": 17}, + "11302": {"name": "BLACK SWAN", "version": 17}, + "11303": {"name": "星詠みとデスペラード", "version": 17}, + "11304": {"name": "Round Round Spinning Around", "version": 17}, + "11305": {"name": "Alcyone", "version": 17}, + "11306": {"name": "Raven Emperor", "version": 17}, + "11307": {"name": "Yorugao", "version": 17}, + "11308": {"name": "ヨミビトシラズ", "version": 17}, + "11309": {"name": "前衛的Landscape", "version": 17}, + "11310": {"name": "Trick tear", "version": 17}, + "11311": {"name": "躯樹の墓守", "version": 17}, + "11312": {"name": "ぱくぱく☆がーる", "version": 17}, + "11313": {"name": "No Limit RED Force", "version": 17}, + "11314": {"name": "LiftOff", "version": 17}, + "11315": {"name": "Falsum Atlantis.", "version": 17}, + "11316": {"name": "We Are Us", "version": 17}, + "11317": {"name": "Limits", "version": 17}, + "11318": {"name": "スピカの天秤", "version": 17}, + "11319": {"name": "ハードコア・シンドローム", "version": 17}, + "11321": {"name": "夜に駆ける", "version": 17}, + "11322": {"name": "うっせぇわ", "version": 17}, + "11323": {"name": "Shooting Stars", "version": 17}, + "11324": {"name": "MOON NIGHTのせいにして", "version": 17}, + "11325": {"name": "テレキャスタービーボーイ", "version": 17}, + "11327": {"name": "僕は空気が嫁ない", "version": 17}, + "11328": {"name": "デッドマンズバラッド", "version": 17}, + "11329": {"name": "イヤホンロマンス", "version": 17}, + "11330": {"name": "竹", "version": 17}, + "11331": {"name": "スーパーシンメトリー", "version": 17}, + "11333": {"name": "Southern Cross", "version": 17}, + "11334": {"name": "Upshift", "version": 17}, + "11335": {"name": "MOBILYS", "version": 17}, + "11336": {"name": "ドラゴンエネルギー", "version": 17}, + "11337": {"name": "セカイはまだ始まってすらいない", "version": 19}, + "11340": {"name": "Sweets Time", "version": 17}, + "11341": {"name": "紅星ミゼラブル~廃憶編", "version": 17}, + "11342": {"name": "幻想に咲いた花", "version": 17}, + "11343": {"name": "マツヨイナイトバグ", "version": 17}, + "11344": {"name": "ポッピンキャンディ☆フィーバー!", "version": 17}, + "11345": {"name": "どぅーまいべすと!", "version": 17}, + "11346": {"name": "春を告げる", "version": 18}, + "11347": {"name": "泥の分際で私だけの大切を奪おうだなんて", "version": 17}, + "11348": {"name": "VOLTAGE", "version": 17}, + "11349": {"name": "失敗作少女", "version": 17}, + "11350": {"name": "悪魔の踊り方", "version": 17}, + "11351": {"name": "ノイローゼ", "version": 17}, + "11352": {"name": "Seize The Day", "version": 17}, + "11353": {"name": "グッバイ宣言", "version": 17}, + "11354": {"name": "春嵐", "version": 17}, + "11355": {"name": "ラグトレイン", "version": 17}, + "11356": {"name": "なだめスかし Negotiation(TVsize)", "version": 17}, + "11358": {"name": "インドア系ならトラックメイカー", "version": 17}, + "11359": {"name": "アカツキアライヴァル", "version": 17}, + "11360": {"name": "リモコン", "version": 17}, + "11361": {"name": "on the rocks", "version": 17}, + "11362": {"name": "響け!CHIREI MY WAY!", "version": 17}, + "11363": {"name": "神々が恋した幻想郷", "version": 17}, + "11364": {"name": "Lia=Fail", "version": 17}, + "11365": {"name": "アンビバレンス", "version": 17}, + "11367": {"name": "veil", "version": 17}, + "11369": {"name": "廻廻奇譚", "version": 17}, + "11370": {"name": "さんさーら!", "version": 17}, + "11371": {"name": "アイドル新鋭隊", "version": 19}, + "11372": {"name": "needLe", "version": 19}, + "11373": {"name": "ヒステリックナイトガール", "version": 17}, + "11374": {"name": "GIGANTØMAKHIA", "version": 17}, + "11375": {"name": "ミルキースター・シューティングスター", "version": 18}, + "11376": {"name": "isophote", "version": 18}, + "11377": {"name": "パラマウント☆ショータイム!!", "version": 18}, + "11378": {"name": "Strive against fate", "version": 18}, + "11379": {"name": "sølips", "version": 18}, + "11380": {"name": "パーフェクション", "version": 18}, + "11381": {"name": "デーモンベット", "version": 18}, + "11382": {"name": "HECATONCHEIR", "version": 18}, + "11383": {"name": "Irresistible", "version": 18}, + "11384": {"name": "HAGAKIRI", "version": 18}, + "11385": {"name": "N3V3R G3T OV3R", "version": 18}, + "11386": {"name": "Swift Swing", "version": 18}, + "11387": {"name": "星空パーティーチューン", "version": 18}, + "11388": {"name": "チューリングの跡", "version": 18}, + "11389": {"name": "Sage", "version": 18}, + "11390": {"name": "OTOGEMA", "version": 17}, + "11391": {"name": "WiPE OUT MEMORIES", "version": 18}, + "11392": {"name": "Metamorphosism", "version": 18}, + "11393": {"name": "白花の天使", "version": 18}, + "11394": {"name": "World's end loneliness", "version": 18}, + "11395": {"name": "Jouez Avec Moi?", "version": 18}, + "11396": {"name": "テリトリーバトル", "version": 18}, + "11398": {"name": "Good bye, Merry-Go-Round.", "version": 18}, + "11399": {"name": "ツクヨミステップ", "version": 18}, + "11400": {"name": "folern", "version": 18}, + "11401": {"name": "コスモポップファンクラブ", "version": 17}, + "11402": {"name": "撩乱乙女†無双劇", "version": 18}, + "11404": {"name": "See The Light", "version": 18}, + "11405": {"name": "エータ・ベータ・イータ", "version": 18}, + "11406": {"name": "愛のシュプリーム!", "version": 18}, + "11407": {"name": "くらべられっ子", "version": 18}, + "11408": {"name": "あの世行きのバスに乗ってさらば。", "version": 18}, + "11409": {"name": "うまぴょい伝説", "version": 18}, + "11410": {"name": "ヴァンパイア", "version": 18}, + "11411": {"name": "エンヴィーベイビー", "version": 18}, + "11412": {"name": "初音天地開闢神話", "version": 18}, + "11413": {"name": "Armageddon", "version": 18}, + "11415": {"name": "Dreadnought", "version": 18}, + "11418": {"name": "enchanted love", "version": 18}, + "11419": {"name": "Caterpillar Song", "version": 18}, + "11420": {"name": "Photon Melodies", "version": 18}, + "11421": {"name": "吾輩よ猫であれ", "version": 18}, + "11422": {"name": " ", "version": 18}, + "11423": {"name": "BLUE ZONE", "version": 18}, + "11424": {"name": "Estahv", "version": 18}, + "11425": {"name": "First Dance", "version": 18}, + "11426": {"name": "Mjölnir", "version": 18}, + "11427": {"name": "Define", "version": 18}, + "11428": {"name": "BANG!", "version": 18}, + "11429": {"name": "Love's Theme of BADASS ~バッド・アス 愛のテーマ~", "version": 18}, + "11430": {"name": "オントロジー", "version": 18}, + "11431": {"name": "剣を抜け!GCCX MAX", "version": 18}, + "11432": {"name": "Princess♂", "version": 18}, + "11433": {"name": "ヒトガタ", "version": 18}, + "11434": {"name": "Spring of Dreams", "version": 18}, + "11435": {"name": "大輪の魂 (feat. AO, 司芭扶)", "version": 18}, + "11436": {"name": "紅に染まる恋の花", "version": 18}, + "11437": {"name": "三妖精SAY YA!!!", "version": 18}, + "11438": {"name": "Reach For The Stars (Re-Colors)", "version": 18}, + "11439": {"name": "ジャンキーナイトタウンオーケストラ", "version": 18}, + "11441": {"name": "終焉逃避行", "version": 18}, + "11443": {"name": "8-EM", "version": 17}, + "11444": {"name": "若い力", "version": 18}, + "11445": {"name": "遺伝子レベル∞スパイラル", "version": 18}, + "11446": {"name": "otorii INNOVATED -[i]3-", "version": 18}, + "11447": {"name": "エゴロック", "version": 18}, + "11448": {"name": "ただ選択があった", "version": 18}, + "11449": {"name": "パズルリボン", "version": 18}, + "11450": {"name": "YONA YONA DANCE", "version": 18}, + "11451": {"name": "Transcend Lights", "version": 18}, + "11452": {"name": "ホシシズク", "version": 19}, + "11453": {"name": "Rainbow Rush Story", "version": 19}, + "11454": {"name": "Tricolor⁂circuS", "version": 19}, + "11455": {"name": "[X]", "version": 19}, + "11456": {"name": "分解収束テイル", "version": 19}, + "11457": {"name": "mystique as iris", "version": 19}, + "11458": {"name": "Rising on the horizon", "version": 19}, + "11459": {"name": "You Mean the World to Me", "version": 19}, + "11460": {"name": "Neon Kingdom", "version": 19}, + "11461": {"name": "#狂った民族2 PRAVARGYAZOOQA", "version": 19}, + "11462": {"name": "VSpook!", "version": 19}, + "11463": {"name": "RIFFRAIN", "version": 19}, + "11464": {"name": "Falling", "version": 19}, + "11465": {"name": "ピリオドサイン", "version": 19}, + "11466": {"name": "群青シグナル", "version": 19}, + "11467": {"name": "Beat Opera op.1", "version": 19}, + "11468": {"name": "星見草", "version": 19}, + "11469": {"name": "\"411Ψ892\"", "version": 19}, + "11470": {"name": "康莊大道", "version": 19}, + "11471": {"name": "蜘蛛の糸", "version": 19}, + "11472": {"name": "Don't Fight The Music", "version": 19}, + "11473": {"name": "Catch Me If You Can", "version": 19}, + "11474": {"name": "MAGNETAR GIRL", "version": 19}, + "11475": {"name": "SUPER AMBULANCE", "version": 19}, + "11477": {"name": "Ghost Dance", "version": 19}, + "11478": {"name": "電光石火", "version": 19}, + "11479": {"name": "Hainuwele", "version": 19}, + "11480": {"name": "アノーイング!さんさんウィーク!", "version": 19}, + "11481": {"name": "アニマル", "version": 19}, + "11482": {"name": "ジレンマ", "version": 19}, + "11483": {"name": "踊", "version": 19}, + "11484": {"name": "残響散歌", "version": 19}, + "11485": {"name": "フォニイ", "version": 19}, + "11486": {"name": "ヴィラン", "version": 19}, + "11487": {"name": "EYE", "version": 19}, + "11488": {"name": "スカーレット警察のゲットーパトロール24時", "version": 19}, + "11489": {"name": "田中", "version": 19}, + "11490": {"name": "Random", "version": 19}, + "11491": {"name": "Luminaria", "version": 19}, + "11492": {"name": "群青讃歌", "version": 20}, + "11493": {"name": "セカイ", "version": 20}, + "11494": {"name": "ワーワーワールド", "version": 20}, + "11495": {"name": "銀のめぐり", "version": 19}, + "11496": {"name": "Destiny Runner", "version": 19}, + "11497": {"name": "アマカミサマ", "version": 19}, + "11498": {"name": "モンダイナイトリッパー!", "version": 19}, + "11499": {"name": "マーシャル・マキシマイザー", "version": 19}, + "11500": {"name": "秋の未確認生物", "version": 19}, + "11501": {"name": "Dive into the ZONe", "version": 19}, + "11502": {"name": "エナドリおいしいソング", "version": 19}, + "11503": {"name": "Baddest", "version": 19}, + "11504": {"name": "ばかみたい【Taxi Driver Edition】", "version": 19}, + "11505": {"name": "れっつ!みらくる☆はーどこあっ!", "version": 19}, + "11506": {"name": "Blank Paper (Prod. TEMPLIME)", "version": 19}, + "11507": {"name": "In my world (Prod. KOTONOHOUSE)", "version": 19}, + "11508": {"name": "アイム・マイヒーロー", "version": 19}, + "11509": {"name": "NightTheater", "version": 19}, + "11510": {"name": "キュートなカノジョ", "version": 19}, + "11511": {"name": "へべれけジャンキー", "version": 19}, + "11512": {"name": "きゅうくらりん", "version": 19}, + "11513": {"name": "回る空うさぎ", "version": 19}, + "11514": {"name": "Lost Desire", "version": 19}, + "11516": {"name": "Aegleseeker", "version": 19}, + "11517": {"name": "最強STRONGER", "version": 19}, + "11518": {"name": "ボッカデラベリタ", "version": 19}, + "11519": {"name": "『んっあっあっ。』", "version": 19}, + "11520": {"name": "独りんぼエンヴィー", "version": 19}, + "11521": {"name": "ロータスイーター", "version": 19}, + "11523": {"name": "ViRTUS", "version": 19}, + "11524": {"name": "Alice's Suitcase", "version": 19}, + "11525": {"name": "ピュグマリオンの咒文", "version": 19}, + "11526": {"name": "トノサマビーム", "version": 20}, + "11527": {"name": "enchanted wanderer", "version": 20}, + "11528": {"name": "Comet Panto Men!", "version": 20}, + "11529": {"name": "ツムギボシ", "version": 20}, + "11530": {"name": "VeRForTe αRtE:VEiN", "version": 20}, + "11532": {"name": "ヱデン", "version": 20}, + "11533": {"name": "にゃーにゃー冒険譚", "version": 20}, + "11534": {"name": "The Great Banquet", "version": 21}, + "11535": {"name": "Redemption", "version": 21}, + "11536": {"name": "Ether Second", "version": 21}, + "11537": {"name": "Straight into the lights", "version": 21}, + "11538": {"name": "アンバークロニクル", "version": 20}, + "11539": {"name": "リフヴェイン", "version": 20}, + "11540": {"name": "Kairos", "version": 20}, + "11541": {"name": "宵の鳥", "version": 20}, + "11542": {"name": "ここからはじまるプロローグ。 (Kanon Remix)", "version": 20}, + "11543": {"name": "モ°ルモ°ル (MZK Skippin' Remix)", "version": 20}, + "11544": {"name": "VERTeX (rintaro soma deconstructed remix)", "version": 20}, + "11545": {"name": "隠密あんみつDX", "version": 20}, + "11546": {"name": "地球", "version": 20}, + "11547": {"name": "Churros Parlor", "version": 20}, + "11548": {"name": "超熊猫的周遊記(ワンダーパンダートラベラー)", "version": 20}, + "11549": {"name": "Trrricksters!!", "version": 19}, + "11550": {"name": "FLUFFY FLASH", "version": 20}, + "11551": {"name": "STARRED HEART", "version": 20}, + "11552": {"name": "Y.Y.Y.計画!!!!", "version": 20}, + "11553": {"name": "Last Kingdom", "version": 20}, + "11554": {"name": "LAMIA", "version": 20}, + "11555": {"name": "ヒバリ", "version": 20}, + "11556": {"name": "Hello, Hologram", "version": 20}, + "11557": {"name": "不機嫌なスリーカード", "version": 20}, + "11558": {"name": "神っぽいな", "version": 20}, + "11559": {"name": "魔法少女とチョコレゐト", "version": 20}, + "11560": {"name": "阿修羅ちゃん", "version": 20}, + "11561": {"name": "おとせサンダー", "version": 20}, + "11562": {"name": "ロウワー", "version": 20}, + "11563": {"name": "キャットラビング", "version": 20}, + "11564": {"name": "リスペク風神", "version": 20}, + "11565": {"name": "Let you DIVE!", "version": 19}, + "11566": {"name": "Knight Rider", "version": 19}, + "11567": {"name": "I’m Here (feat. Merry Kirk-Holmes)", "version": 21}, + "11568": {"name": "INTERNET OVERDOSE", "version": 19}, + "11569": {"name": "魂のルフラン", "version": 20}, + "11570": {"name": "Shooting Shower~DANCE TIME(シンディ)~", "version": 20}, + "11571": {"name": "Lights of Muse", "version": 20}, + "11572": {"name": "tape/stop/night", "version": 20}, + "11573": {"name": "Final Step!", "version": 20}, + "11574": {"name": "The 90's Decision", "version": 20}, + "11575": {"name": "僕の和風本当上手", "version": 20}, + "11576": {"name": "Cthugha", "version": 20}, + "11577": {"name": "はんぶんこ", "version": 20}, + "11578": {"name": "PUPA", "version": 21}, + "11580": {"name": "解けないように", "version": 21}, + "11583": {"name": "インターネットサバイバー", "version": 20}, + "11584": {"name": "コンティニュー! feat. 藍月なくる", "version": 20}, + "11585": {"name": "Sunday Night feat Kanata.N", "version": 20}, + "11586": {"name": "PERSONA feat. PANXI", "version": 20}, + "11587": {"name": "Halfway(>∀<)", "version": 20}, + "11588": {"name": "Complex Mind", "version": 20}, + "11589": {"name": "DROPS feat. Such", "version": 20}, + "11590": {"name": "あつすぎの歌", "version": 20}, + "11591": {"name": "ULTRA SYNERGY MATRIX", "version": 20}, + "11592": {"name": "花となれ", "version": 20}, + "11593": {"name": "私のドッペルゲンガー", "version": 20}, + "11594": {"name": "BULK UP (GAME EXCLUSIVE EDIT)", "version": 20}, + "11596": {"name": "Vallista", "version": 20}, + "11597": {"name": "ノンブレス・オブリージュ", "version": 21}, + "11598": {"name": "テオ", "version": 20}, + "11599": {"name": "まにまに", "version": 20}, + "11600": {"name": "感情ディシーブ", "version": 20}, + "11601": {"name": "ド屑", "version": 20}, + "11602": {"name": "絶対敵対メチャキライヤー", "version": 20}, + "11603": {"name": "つるぺったん", "version": 20}, + "11604": {"name": "『ウソテイ』 ~一回戦せりなvsしろなvsなずな~", "version": 20}, + "11605": {"name": "Bad Apple!! feat.nomico ~五十嵐 撫子 Ver.~", "version": 20}, + "11606": {"name": "Snow Colored Score", "version": 20}, + "11607": {"name": "MAGENTA POTION", "version": 20}, + "11608": {"name": "NOIZY BOUNCE", "version": 21}, + "11609": {"name": "サンバディ!", "version": 21}, + "11610": {"name": "Horoscope Express", "version": 21}, + "11611": {"name": "Party☆People☆Princess", "version": 21}, + "11612": {"name": "Latent Kingdom", "version": 21}, + "11613": {"name": "Mystic Parade", "version": 21}, + "11614": {"name": "Cry Cry Cry", "version": 21}, + "11615": {"name": "ぽわわん劇場", "version": 21}, + "11616": {"name": "my flow", "version": 21}, + "11617": {"name": "POWER OF UNITY", "version": 21}, + "11618": {"name": "Energizing Flame", "version": 21}, + "11619": {"name": "KHYMΞXΛ", "version": 20}, + "11620": {"name": "フェイクフェイス・フェイルセイフ", "version": 21}, + "11621": {"name": "ふらふらふら、", "version": 21}, + "11622": {"name": "シックスプラン", "version": 21}, + "11623": {"name": "フタタビ", "version": 21}, + "11624": {"name": "SQUAD-Phvntom-", "version": 21}, + "11625": {"name": "GEOMETRIC DANCE", "version": 21}, + "11626": {"name": "Ring", "version": 21}, + "11627": {"name": "インパアフェクシオン・ホワイトガアル", "version": 21}, + "11628": {"name": "WE'RE BACK!!", "version": 21}, + "11629": {"name": "熱異常", "version": 21}, + "11630": {"name": "1000年生きてる", "version": 21}, + "11631": {"name": "IMAWANOKIWA", "version": 21}, + "11632": {"name": "Play merrily NEO", "version": 21}, + "11633": {"name": "OMAKENO Stroke", "version": 21}, + "11634": {"name": "青春コンプレックス", "version": 21}, + "11635": {"name": "アンダーキッズ", "version": 21}, + "11636": {"name": "さくゆいたいそう", "version": 21}, + "11637": {"name": "バグ", "version": 21}, + "11638": {"name": "星界ちゃんと可不ちゃんのおつかい合騒曲", "version": 21}, + "11639": {"name": "Burn My Soul", "version": 21}, + "11640": {"name": "with U", "version": 21}, + "11641": {"name": "Love Kills U", "version": 21}, + "11642": {"name": "DANGEROOOOUS JUNGLE", "version": 21}, + "11643": {"name": "天使光輪", "version": 21}, + "11644": {"name": "story", "version": 21}, + "11645": {"name": "トンデモワンダーズ", "version": 21}, + "11646": {"name": "神威", "version": 21}, + "11647": {"name": "ozma", "version": 21}, + "11648": {"name": "月面基地", "version": 21}, + "11650": {"name": "ずんだもんの朝食 〜目覚ましずんラップ〜", "version": 21}, + "11651": {"name": "あなたは世界の終わりにずんだを食べるのだ", "version": 21}, + "11652": {"name": "HUMANBORG", "version": 21}, + "11653": {"name": "ULTRA POWER", "version": 21}, + "11654": {"name": "ジェヘナ", "version": 21}, + "11655": {"name": "ツギハギスタッカート", "version": 21}, + "11656": {"name": "HANIPAGANDA", "version": 21}, + "11657": {"name": "Rush-Hour", "version": 21}, + "11658": {"name": "QUEEN", "version": 21}, + "11659": {"name": "天使の翼。", "version": 21}, + "11660": {"name": "さよならプリンセス", "version": 21}, + "11661": {"name": "Apollo", "version": 21}, + "11662": {"name": "raputa", "version": 21}, + "11663": {"name": "系ぎて", "version": 21}, + "11664": {"name": "アルカンシエル", "version": 21}, + "11665": {"name": "RAD DOGS", "version": 22}, + "11666": {"name": "アイディスマイル", "version": 22}, + "11667": {"name": "にっこり^^調査隊のテーマ", "version": 22}, + "11668": {"name": "PinqPiq (xovevox Remix)", "version": 21}, + "11669": {"name": "エスオーエス", "version": 22}, + "11670": {"name": "のじゃロリック", "version": 22}, + "11671": {"name": "Edelweiss", "version": 22}, + "11672": {"name": "QuiQ", "version": 22}, + "11673": {"name": "IF:U", "version": 22}, + "11674": {"name": "Cider P@rty", "version": 22}, + "11675": {"name": "勦滅", "version": 22}, + "11676": {"name": "Lunatic Vibes", "version": 22}, + "11677": {"name": "Bloody Trail", "version": 22}, + "11678": {"name": "RE:INCARNATED DRAGNER", "version": 22}, + "11679": {"name": "Beginning together!", "version": 22}, + "11680": {"name": "Shining Ray ~僕らの絆~", "version": 22}, + "11681": {"name": "DEVOTION", "version": 22}, + "11682": {"name": "Geranium", "version": 22}, + "11683": {"name": "The Cursed Doll", "version": 22}, + "11684": {"name": "RondeauX of RagnaroQ", "version": 22}, + "11685": {"name": "ℝ∈Χ LUNATiCA", "version": 22}, + "11686": {"name": "Λzure Vixen", "version": 21}, + "11687": {"name": "MarbleBlue.", "version": 22}, + "11688": {"name": "まっすぐ→→→ストリーム!", "version": 22}, + "11689": {"name": "最っ高のエンタメだ!!", "version": 22}, + "11690": {"name": "Baqeela", "version": 22}, + "11691": {"name": "Ai C", "version": 22}, + "11692": {"name": "INTERNET YAMERO", "version": 22}, + "11693": {"name": "過去を喰らう", "version": 22}, + "11694": {"name": "強風オールバック", "version": 22}, + "11695": {"name": "寝起きヤシの木", "version": 22}, + "11696": {"name": "アイドル", "version": 22}, + "11697": {"name": "ラビットホール", "version": 22}, + "11698": {"name": "酔いどれ知らず", "version": 22}, + "11699": {"name": "新人類", "version": 22}, + "11700": {"name": "Freak Out Hr.", "version": 22}, + "11701": {"name": "さよならヒストリー", "version": 22}, + "11702": {"name": "New York Back Raise", "version": 22}, + "11704": {"name": "DUAL ROZES", "version": 22}, + "11705": {"name": "VOLT", "version": 22}, + "11706": {"name": "tiny tales continue", "version": 22}, + "11707": {"name": "ouroboros -twin stroke of the end-", "version": 22}, + "11708": {"name": "カーニバルハッピー", "version": 22}, + "11709": {"name": "つづみぐさ", "version": 22}, + "11710": {"name": "サイバーサンダーサイダー", "version": 22}, + "11711": {"name": "おくすり飲んで寝よう", "version": 22}, + "11712": {"name": "バラバラ〜仮初レインボーローズ〜", "version": 22}, + "11713": {"name": "QUATTUORUX", "version": 22}, + "11714": {"name": "匿名M", "version": 22}, + "11715": {"name": "ライアーダンサー", "version": 22}, + "11716": {"name": "バカ通信", "version": 22}, + "11717": {"name": "LOSTPHANTASIA", "version": 22}, + "11718": {"name": "Bad Apple!! feat.nomico (Tetsuya Komuro Remix)", "version": 22}, + "11720": {"name": "Re:Unknown X", "version": 22}, + "11721": {"name": "INFiNiTE ENERZY -Overdoze-", "version": 22}, + "11722": {"name": "Make Up Your World feat. キョンシーのCiちゃん & らっぷびと", "version": 22}, + "11723": {"name": "BOOM! BOOM!! BOOM!!!", "version": 22}, + "11724": {"name": "Everybody Say HARDCORE TANO*C", "version": 22}, + "11725": {"name": "ミラクルポップ☆アドベンチャー!!!!!", "version": 22}, + "11726": {"name": "Trust", "version": 22}, + "11727": {"name": "ラヴィ", "version": 22}, + "11728": {"name": "スティールユー", "version": 22}, + "11729": {"name": "オシオキGIMMICK!!", "version": 22}, + "11730": {"name": "偉大なる悪魔は実は大天使パトラちゃん様なのだ!", "version": 22}, + "11731": {"name": "シュガーホリック", "version": 22}, + "11733": {"name": "MUSIC PЯAYER", "version": 22}, + "11734": {"name": "壱雫空", "version": 22}, + "11735": {"name": "ザムザ", "version": 22}, + "11736": {"name": "プリズム△▽リズム", "version": 23}, + "11737": {"name": "パラドクスイヴ", "version": 23}, + "11738": {"name": "YKWTD", "version": 23}, + "11739": {"name": "184億回のマルチトニック", "version": 23}, + "11740": {"name": "果ての空、僕らが見た光。", "version": 23}, + "11741": {"name": "Cryptarithm", "version": 23}, + "11746": {"name": "Divide et impera!", "version": 22}, + "11754": {"name": "Elemental Ethnic", "version": 23}, + "11755": {"name": "美夜月鏡", "version": 23}, + "11760": {"name": "勇者", "version": 23}, + "11761": {"name": "病み垢ステロイド", "version": 23}, + "11763": {"name": "ダーリンダンス", "version": 23}, + "11764": {"name": "唱", "version": 23}, + "11765": {"name": "愛包ダンスホール", "version": 23}, + "11766": {"name": "マツケンサンバⅡ", "version": 23}, + "11767": {"name": "デビルじゃないもん", "version": 23}, + "11769": {"name": "ずんだパーリナイ", "version": 23}, + "11770": {"name": "Ultimate taste", "version": 23}, + "11771": {"name": "Chronomia", "version": 23}, + "11772": {"name": "人マニア", "version": 23}, + "11774": {"name": "メイトなやつら(FEAT. 天開司, 佐藤ホームズ, あっくん大魔王 & 歌衣メイカ)", "version": 23}, + "11776": {"name": "ゆけむり魂温泉 II", "version": 23}, + "11777": {"name": "人間が大好きなこわれた妖怪の唄", "version": 23}, + "11784": {"name": "イガク", "version": 23}, + "11786": {"name": "Empire of Winter", "version": 23}, + "11787": {"name": "迷える音色は恋の唄", "version": 23}, + "11788": {"name": "Löschen", "version": 23}, + "11789": {"name": "Abstruse Dilemma", "version": 23}, + "11805": {"name": "リアライズ", "version": 23}, + "100017": {"name": "[星]Future", "version": 21}, + "100018": {"name": "[協]Love You", "version": 21}, + "100022": {"name": "[蔵]In Chaos", "version": 21}, + "100042": {"name": "[宴]jelly", "version": 21}, + "100056": {"name": "[耐]ウッーウッーウマウマ(゚∀゚)", "version": 21}, + "100070": {"name": "[即]ジングルベル", "version": 21}, + "100082": {"name": "[光]Reach For The Stars", "version": 21}, + "100101": {"name": "[逆]おちゃめ機能", "version": 21}, + "100118": {"name": "[逆]犬日和。", "version": 21}, + "100121": {"name": "[撫]スイートマジック", "version": 21}, + "100132": {"name": "[蛸]Turn around", "version": 21}, + "100138": {"name": "[蔵]Lionheart", "version": 21}, + "100145": {"name": "[宴]Starlight Disco", "version": 21}, + "100191": {"name": "[蔵]脳漿炸裂ガール", "version": 21}, + "100199": {"name": "[蛸]チルノのパーフェクトさんすう教室", "version": 21}, + "100201": {"name": "[宴]魔理沙は大変なものを盗んでいきました", "version": 21}, + "100206": {"name": "[星]しゅわスパ大作戦☆", "version": 21}, + "100207": {"name": "[蛸]時空を超えて久しぶり!", "version": 21}, + "100220": {"name": "[宴]セガサターン起動音[H.][Remix]", "version": 21}, + "100224": {"name": "[傾]ウサテイ", "version": 21}, + "100227": {"name": "[は]Garakuta Doll Play", "version": 21}, + "100230": {"name": "[宴]CYCLES", "version": 21}, + "100259": {"name": "[協]ぽっぴっぽー", "version": 21}, + "100282": {"name": "[撫]からくりピエロ", "version": 21}, + "100305": {"name": "[星]POP STAR", "version": 21}, + "100308": {"name": "[傾]Back 2 Back", "version": 21}, + "100319": {"name": "[藏]幻想のサテライト", "version": 21}, + "100320": {"name": "[即]最速最高シャッターガール", "version": 21}, + "100322": {"name": "[蔵]林檎華憐歌", "version": 21}, + "100326": {"name": "[宴]コトバ・カラフル", "version": 21}, + "100327": {"name": "[狂]タカハせ!名人マン", "version": 21}, + "100339": {"name": "[宴]DRAGONLADY", "version": 21}, + "100363": {"name": "[宴]Oshama Scramble!", "version": 21}, + "100364": {"name": "[覚]D✪N’T ST✪P R✪CKIN’", "version": 21}, + "100380": {"name": "[蔵]どうしてこうなった", "version": 21}, + "100385": {"name": "[蔵]Ignis Danse", "version": 21}, + "100413": {"name": "[覚]キャプテン・ムラサのケツアンカー", "version": 21}, + "100422": {"name": "[覚]LANCE", "version": 21}, + "100429": {"name": "[宴]Wonderland Wars オープニング", "version": 21}, + "100446": {"name": "[宴]GEMINI -M-", "version": 21}, + "100456": {"name": "[蔵]Glorious Crown", "version": 21}, + "100496": {"name": "[藏]AMAZING MIGHTYYYY!!!!", "version": 21}, + "100508": {"name": "[協]恋愛裁判", "version": 21}, + "100513": {"name": "[耐]だんだん早くなる", "version": 21}, + "100516": {"name": "[狂]ココロ", "version": 21}, + "100517": {"name": "[覚]東京リアルワールド", "version": 21}, + "100524": {"name": "[協]Hand in Hand", "version": 21}, + "100548": {"name": "[疑]DETARAME ROCK&ROLL THEORY", "version": 21}, + "100571": {"name": "[即]Our Wrenally", "version": 21}, + "100573": {"name": "[宴]四月の雨", "version": 21}, + "100598": {"name": "[蛸]すーぱーぬこになりたい", "version": 21}, + "100603": {"name": "[星]その群青が愛しかったようだった", "version": 21}, + "100614": {"name": "[宴]夢花火", "version": 21}, + "100617": {"name": "[協]オトヒメモリー☆ウタゲーション", "version": 21}, + "100689": {"name": "[蔵]Credits", "version": 21}, + "100700": {"name": "[協]疾走あんさんぶる", "version": 21}, + "100712": {"name": "[蛸]ミラクル・ショッピング", "version": 21}, + "100749": {"name": "[蔵]Ievan Polkka", "version": 21}, + "100772": {"name": "[狂]FREEDOM DiVE (tpz Overcute Remix", "version": 90}, + "100775": {"name": "[宴]ジンギスカン", "version": 21}, + "100845": {"name": "[宴]怒槌~光吉猛修一部謎~", "version": 21}, + "100846": {"name": "[宴]Oshama Scramble! (Cranky Remix)", "version": 21}, + "100851": {"name": "[宴]犬日和。(はっぴー歌唱Ver)", "version": 21}, + "100854": {"name": "[宴]全世界共通リズム感テスト", "version": 21}, + "100998": {"name": "[宴]Oshama Scramble! (Cranky Remix)", "version": 22}, + "100999": {"name": "[宴]全世界共通リズム感テスト", "version": 21}, + "101222": {"name": "[光]BREaK! BREaK! BREaK!", "version": 21}, + "101516": {"name": "[藏]Aegleseeker", "version": 21}, + "110082": {"name": "[藏]Reach For The Stars", "version": 21}, + "110114": {"name": "[耐]Space Harrier Main Theme [Reborn]", "version": 21}, + "110227": {"name": "[J]Garakuta Doll Play", "version": 22}, + "110281": {"name": "[回]ダブルラリアット", "version": 23}, + "110417": {"name": "[習]ウミユリ海底譚", "version": 23}, + "110532": {"name": "[奏]洗脳", "version": 23}, + "110553": {"name": "[奏]チュルリラ・チュルリラ・ダッダッダ!", "version": 22}, + "110579": {"name": "[協]ポップミュージックは僕のもの", "version": 21}, + "110606": {"name": "[右]The wheel to the right", "version": 21}, + "110631": {"name": "[逆]KING is BACK!!", "version": 21}, + "110647": {"name": "[協]エイリアンエイリアン", "version": 22}, + "110793": {"name": "[疑]ロキ", "version": 22}, + "111069": {"name": "[協]太陽系デスコ", "version": 23}, + "111222": {"name": "[光]BREaK! BREaK! BREaK!", "version": 21}, + "111325": {"name": "[奏]テレキャスタービーボーイ", "version": 22}, + "111355": {"name": "[協]ラグトレイン", "version": 21}, + "111359": {"name": "[奏]アカツキアライヴァル", "version": 21}, + "111365": {"name": "[奏]アンビバレンス", "version": 23}, + "111433": {"name": "[奏]ヒトガタ", "version": 22}, + "111437": {"name": "[協]三妖精SAY YA!!!", "version": 22}, + "111488": {"name": "[覚]スカーレット警察のゲットーパトロール24時", "version": 22}, + "111513": {"name": "[回]回る空うさぎ", "version": 21}, + "111516": {"name": "[蔵]Aegleseeker", "version": 21}, + "111577": {"name": "[両]はんぶんこ", "version": 22}, + "111597": {"name": "[息]ノンブレス・オブリージュ", "version": 22}, + "111634": {"name": "[協]青春コンプレックス", "version": 21}, + "111636": {"name": "[奏]さくゆいたいそう", "version": 22}, + "120227": {"name": "[r]Garakuta Doll Play", "version": 22}, + "121634": {"name": "[協]青春コンプレックス", "version": 21}, + "130227": {"name": "[玉]Garakuta Doll Play", "version": 22}, + "140227": {"name": "[某]Garakuta Doll Play", "version": 22}, + "200082": {"name": "[蔵]Reach For The Stars", "version": 21}, + "200227": {"name": "[宴]Garakuta Doll Play No.2", "version": 21}, + "200429": {"name": "[宴]Wonderland Wars オープニング", "version": 21}, + "200854": {"name": "[宴]全世界共通リズム感テスト", "version": 21}, + "200999": {"name": "[宴]全世界共通リズム感テスト", "version": 21}, + "300227": {"name": "[宴]Garakuta Doll Play No.3", "version": 21}, + "300429": {"name": "[宴]Wonderland Wars オープニング", "version": 21}, + "400227": {"name": "[宴]Garakuta Doll Play No.4", "version": 21}, + "400429": {"name": "[宴]Wonderland Wars オープニング", "version": 21}, + "500227": {"name": "[宴]Garakuta Doll Play No.5", "version": 21}, + "500429": {"name": "[宴]Wonderland Wars オープニング", "version": 21}, + "600429": {"name": "[宴]Wonderland Wars オープニング", "version": 21} +} \ No newline at end of file diff --git a/backend/ForceLogout.py b/backend/ForceLogout.py new file mode 100644 index 0000000..1870460 --- /dev/null +++ b/backend/ForceLogout.py @@ -0,0 +1,107 @@ +# 解小黑屋实现 +# 仍十分不完善,不建议使用 +import time +from datetime import datetime + +import rapidjson as json + +from GetPreview import apiGetUserPreview +from HelperLogInOut import apiLogout +from loguru import logger + +from MyConfig import testUid2 + + +def isUserLoggedIn(userId): + isLogin = json.loads(apiGetUserPreview(userId, True))["isLogin"] + logger.debug(f"用户 {userId} 是否登录: {isLogin}") + return isLogin + + +def getHumanReadableTime(unixTime): + """将 Unix 时间戳转换为人类可读的时间""" + # 减一个小时,因为舞萌貌似是 UTC+9 + timestamp = int(unixTime) - 3600 + return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") + + +def getMaimaiUNIXTime(mmddhhmmss, year=2025): + """ + 解析用户输入的 MMDDHHMMSS 格式的时间,返回 Unix 时间戳 + 时间会被推后一个小时,因为舞萌貌似是 UTC+9? + """ + # date_time_str = f"{year}{mmddhhmmss}" + # 加上一个小时 + date_time_str = ( + f"{year}{mmddhhmmss[:4]}{int(mmddhhmmss[4:6]) + 1:02}{mmddhhmmss[6:]}" + ) + date_time_obj = datetime.strptime(date_time_str, "%Y%m%d%H%M%S") + # 将 datetime 对象转换为 Unix 时间戳 + unix_timestamp = int(time.mktime(date_time_obj.timetuple())) + logger.info(f"转换出了时间戳: {unix_timestamp}") + return unix_timestamp + + +def logOut(userId, Timestamp): + """极其简单的登出实现,成功返回 True,失败返回 False + 注意:不会检查用户是否真的登出了,只会尝试登出""" + try: + if apiLogout(Timestamp, userId, True)["returnCode"] == 1: + # 成功送出了登出请求 + logger.debug(f"已成功尝试登出用户 {userId}") + return True + except Exception: + logger.error(f"登出用户 {userId} 的时候发生了错误") + return False + + +def isCorrectTimestamp(timestamp, userId): + """ + 动作:给定一个时间戳,用它尝试登出用户,然后检查用户是否成功登出。 + 如果用户成功登出,返回 True;否则返回 False。 + """ + if not logOut(userId, timestamp): + logger.error(f"用时间戳 {timestamp} 登出用户 {userId} 的时候发生了错误") + return False + isLoggedOut = not isUserLoggedIn(userId) + logger.debug(f"时间戳 {timestamp} 是否正确: {isLoggedOut}") + return isLoggedOut + + +def findCorrectTimestamp(timestamp, userId, max_attempts=600): + # 初始化偏移量 + offset = 1 + attempts = 0 + + while attempts < max_attempts: + # 尝试当前时间戳 + currentTryTimestamp = timestamp + if isCorrectTimestamp(currentTryTimestamp, userId): + logger.info(f"正确的时间戳: {currentTryTimestamp}") + return currentTryTimestamp + + # 增加偏移量尝试 + currentTryTimestamp = timestamp + offset + if isCorrectTimestamp(currentTryTimestamp, userId): + logger.info(f"正确的时间戳(在给定时间以后): {currentTryTimestamp}") + return currentTryTimestamp + + # 减少偏移量尝试 + currentTryTimestamp = timestamp - offset + if isCorrectTimestamp(currentTryTimestamp, userId): + logger.info(f"正确的时间戳(在给定时间以前): {currentTryTimestamp}") + return currentTryTimestamp + + # 增加尝试次数和偏移量 + attempts += 2 + offset += 1 + + logger.error(f"无法找到正确的时间戳,尝试次数超过了 {max_attempts}") + return None + + +if __name__ == "__main__": + human_time = "0207155500" + beginTimestamp = getMaimaiUNIXTime(human_time) + print(f"我们将开始用这个时间戳开始尝试: {beginTimestamp}") + correctTimestamp = findCorrectTimestamp(beginTimestamp, testUid2) diff --git a/backend/GetAny.py b/backend/GetAny.py new file mode 100644 index 0000000..31a0d98 --- /dev/null +++ b/backend/GetAny.py @@ -0,0 +1,31 @@ +# 获取用户简略预览数据的 API 实现,此 API 无需任何登录即可调取 + +from loguru import logger +import rapidjson as json +from API_TitleServer import apiSDGB + + +def apiGetAny( + userId, + apiName: str, + noLog: bool = False, +) -> str: + data = json.dumps({"userId": int(userId)}) + preview_result = apiSDGB(data, apiName, userId, noLog) + return preview_result + + +# CLI 示例 +if __name__ == "__main__": + userId = input("请输入用户 ID:") + # userId = testUid8 + # print(apiGetAny(userId, "GetUserRatingApi")) + # print(apiGetAny(userId, "GetUserPreviewApi")) + + for type in ["course", "extend", "character", "activity", "charge", "option", "region"]: + try: + data = apiGetAny(userId, f"GetUser{type.title()}Api", noLog=True) + except Exception as e: + logger.error(f"failed when scraping {type}: {e}") + else: + print(f"{type}:", json.dumps(json.loads(data), ensure_ascii=False, indent=4)) diff --git a/backend/GetPreview.py b/backend/GetPreview.py new file mode 100644 index 0000000..2cc61d1 --- /dev/null +++ b/backend/GetPreview.py @@ -0,0 +1,18 @@ +# 获取用户简略预览数据的 API 实现,此 API 无需任何登录即可调取 + +import rapidjson as json +from API_TitleServer import apiSDGB + + +def apiGetUserPreview(userId, noLog: bool = False) -> str: + data = json.dumps({"userId": int(userId)}) + preview_result = apiSDGB(data, "GetUserPreviewApi", userId, noLog) + return preview_result + + +# CLI 示例 +if __name__ == "__main__": + userId = input("请输入用户 ID:") + # userId = testUid8 + print(apiGetUserPreview(userId)) + print(apiSDGB("{}", "Ping", userId, False)) diff --git a/backend/GetPreview2024.py b/backend/GetPreview2024.py new file mode 100644 index 0000000..cf964f6 --- /dev/null +++ b/backend/GetPreview2024.py @@ -0,0 +1,21 @@ +# 获取用户简略预览数据的 API 实现,此 API 无需任何登录即可调取 + +import rapidjson as json + +from API_TitleServer import apiSDGB_2024, use2024Api + +use2024Api() + + +def apiGetUserPreview(userId, noLog: bool = False) -> str: + data = json.dumps({"userId": int(userId)}) + preview_result = apiSDGB_2024(data, "GetUserPreviewApi", userId, noLog) + return preview_result + + +# CLI 示例 +if __name__ == "__main__": + userId = input("请输入用户 ID:") + # userId = testUid8 + # print(apiGetUserPreview(userId)) + print(apiSDGB_2024("{}", "Ping", userId, False)) diff --git a/backend/GetRating.py b/backend/GetRating.py new file mode 100644 index 0000000..7893049 --- /dev/null +++ b/backend/GetRating.py @@ -0,0 +1,18 @@ +# 获取用户简略预览数据的 API 实现,此 API 无需任何登录即可调取 + +import rapidjson as json +from API_TitleServer import apiSDGB + + +def apiGetUserPreview(userId, noLog: bool = False) -> str: + data = json.dumps({"userId": int(userId)}) + preview_result = apiSDGB(data, "GetUserRatingApi", userId, noLog) + return preview_result + + +# CLI 示例 +if __name__ == "__main__": + userId = input("请输入用户 ID:") + # userId = testUid8 + print(apiGetUserPreview(userId)) + print(apiSDGB("{}", "Ping", userId, False)) diff --git a/backend/GetUserAll.py b/backend/GetUserAll.py new file mode 100644 index 0000000..d954406 --- /dev/null +++ b/backend/GetUserAll.py @@ -0,0 +1,34 @@ +from loguru import logger + +from HelperFullPlay import implFullPlayAction +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from MyConfig import testUid8 + +if __name__ == "__main__": + userId = int(input("type user id: ").strip() or "0") or testUid8 + currentLoginTimestamp = generateTimestamp() + loginResult = apiLogin(currentLoginTimestamp, userId) + + if loginResult["returnCode"] != 1: + logger.info("登录失败") + exit() + try: + items = [23] + musicData = { + "musicId": 11538, # Amber Chronicle + "level": 0, + "playCount": 1, + "achievement": 0, + "comboStatus": 0, + "syncStatus": 0, + "deluxscoreMax": 0, + "scoreRank": 0, + "extNum1": 0, + } + implFullPlayAction( + userId, currentLoginTimestamp, loginResult, musicData, {}, debugMode=True + ) + logger.info(apiLogout(currentLoginTimestamp, userId)) + finally: + logger.info(apiLogout(currentLoginTimestamp, userId)) + # logger.warning("Error") diff --git a/backend/GetUserData.py b/backend/GetUserData.py new file mode 100644 index 0000000..c2fae85 --- /dev/null +++ b/backend/GetUserData.py @@ -0,0 +1,30 @@ +# 获取用户简略预览数据的 API 实现,此 API 无需任何登录即可调取 + +from loguru import logger +import rapidjson as json + +from API_TitleServer import apiSDGB +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from MyConfig import testUid8 + + +def apiGetUserData(userId, noLog: bool = False) -> str: + data = json.dumps({"userId": int(userId)}) + preview_result = apiSDGB(data, "GetUserDataApi", userId, noLog) + return preview_result + + +if __name__ == "__main__": + userId = int(input("type user id: ").strip() or "0") or testUid8 + currentLoginTimestamp = generateTimestamp() + loginResult = apiLogin(currentLoginTimestamp, userId) + + if loginResult["returnCode"] != 1: + logger.info("登录失败") + exit() + try: + logger.info(apiGetUserData(userId, noLog=False)) + logger.info(apiLogout(currentLoginTimestamp, userId)) + finally: + logger.info(apiLogout(currentLoginTimestamp, userId)) + # logger.warning("Error") diff --git a/backend/HashEntertainment/All_API_Names.txt b/backend/HashEntertainment/All_API_Names.txt new file mode 100644 index 0000000..937d1b8 --- /dev/null +++ b/backend/HashEntertainment/All_API_Names.txt @@ -0,0 +1,43 @@ +GetUserCardApi +GetUserCharacterApi +GetUserChargeApi +GetUserCourseApi +GetUserDataApi +GetUserExtendApi +GetUserFavoriteApi +GetUserFriendSeasonRankingApi +GetUserGhostApi +GetUserItemApi +GetGameChargeApi +GetUserLoginBonusApi +GetGameEventApi +GetUserMapApi +GetGameNgMusicIdApi +GetUserMusicApi +GetGameRankingApi +GetGameSettingApi +GetUserOptionApi +GetUserPortraitApi +GetGameTournamentInfoApi +UserLogoutApi +GetUserPreviewApi +GetTransferFriendApi +GetUserRatingApi +GetUserActivityApi +GetUserRecommendRateMusicApi +GetUserRecommendSelectMusicApi +GetUserRegionApi +GetUserScoreRankingApi +UploadUserPhotoApi +UploadUserPlaylogApi +UploadUserPortraitApi +UpsertClientBookkeepingApi +UpsertClientSettingApi +UpsertClientTestmodeApi +UpsertClientUploadApi +UpsertUserAllApi +UpsertUserChargelogApi +UserLoginApi +Ping +GetUserFavoriteItemApi +GetGameNgWordListApi diff --git a/backend/HelperFullPlay.py b/backend/HelperFullPlay.py new file mode 100644 index 0000000..15bad3d --- /dev/null +++ b/backend/HelperFullPlay.py @@ -0,0 +1,132 @@ +import rapidjson as json +from loguru import logger + +from API_TitleServer import ( + SDGBRequestError, + SDGBApiError, + apiSDGB, + calcPlaySpecial, +) +from HelperGetUserThing import implGetUser_ +from HelperUploadUserPlayLog import apiUploadUserPlaylog +from HelperUserAll import generateFullUserAll + + +def generateMusicData(): + """生成一份占位的音乐数据""" + return { + "musicId": 834, # PANDORA PARADOXXX + "level": 4, + "playCount": 1, + "achievement": 0, + "comboStatus": 0, + "syncStatus": 0, + "deluxscoreMax": 0, + "scoreRank": 0, + "extNum1": 0, + } + + +def applyUserAllPatches(userAll, patches): + """ + 递归地将给定的补丁应用到用户数据的各个层次。 + + :param userAll: 原始用户数据 + :param patches: 包含所有patch的字典 + """ + for key, value in patches.items(): + if ( + isinstance(value, dict) + and key in userAll + and isinstance(userAll[key], dict) + ): + # 如果patch的值是字典,并且userAll中对应的key也是字典,递归处理 + applyUserAllPatches(userAll[key], value) + elif ( + isinstance(value, list) + and key in userAll + and isinstance(userAll[key], list) + ): + # 如果值是列表,进行详细的更新处理 + for i, patch_item in enumerate(value): + if ( + i < len(userAll[key]) + and isinstance(patch_item, dict) + and isinstance(userAll[key][i], dict) + ): + # 如果列表项是字典,更新字典中的字段 + applyUserAllPatches(userAll[key][i], patch_item) + elif i >= len(userAll[key]): + # 如果patch的列表比userAll的列表长,追加新的元素 + userAll[key].append(patch_item) + else: + # 否则直接更新或添加key + userAll[key] = value + + +def implFullPlayAction( + userId: int, + currentLoginTimestamp: int, + currentLoginResult, + musicData, + userAllPatches, + debugMode=False, +): + """ + 一份完整的上机实现,可以打 patch 来实现各种功能 + 需要在外部先登录并传入登录结果 + """ + + # 取得 UserData + currentUserData = implGetUser_("Data", userId) + currentUserData2 = currentUserData["userData"] + + # 构建并上传一个游玩记录 + currentUploadUserPlaylogApiResult = apiUploadUserPlaylog( + userId, musicData, currentUserData2, currentLoginResult["loginId"] + ) + logger.debug(f"上传 UserPlayLog 结果: {currentUploadUserPlaylogApiResult}") + + # 构建并上传 UserAll + retries = 0 + while retries < 3: + # 计算一个特殊数 + currentPlaySpecial = calcPlaySpecial() + # 生成出 UserAll + currentUserAll = generateFullUserAll( + userId, + currentLoginResult, + currentLoginTimestamp, + currentUserData2, + currentPlaySpecial, + ) + # 应用参数里的补丁 + applyUserAllPatches(currentUserAll, userAllPatches) + + # 调试模式下直接输出数据 + if debugMode: + logger.debug( + "调试模式:构建出的 UserAll 数据:" + + json.dumps(currentUserAll, indent=4) + ) + logger.info("Bye!") + return + + # 建构 Json 数据 + data = json.dumps(currentUserAll) + # 开始上传 UserAll + try: + currentUserAllResult = json.loads(apiSDGB(data, "UpsertUserAllApi", userId)) + except SDGBRequestError: + logger.warning("上传 UserAll 出现 500. 重建数据.") + retries += 1 + continue + except Exception: + raise SDGBApiError("邪门错误") + # 成功上传后退出循环 + break + else: # 重试次数超过3次 + raise SDGBRequestError + + logger.info("上机:结果:" + str(currentUserAllResult)) + return currentUserAllResult diff --git a/backend/HelperGetUserMusicDetail.py b/backend/HelperGetUserMusicDetail.py new file mode 100644 index 0000000..0e86d03 --- /dev/null +++ b/backend/HelperGetUserMusicDetail.py @@ -0,0 +1,63 @@ +# 获取用户成绩的各种实现 +import rapidjson as json +from loguru import logger + +from HelperMusicDB import getMusicTitle +from API_TitleServer import apiSDGB +from MyConfig import testUid + +# 日志设置 +# log_level = "DEBUG" +# log_format = "{time:YYYY-MM-DD HH:mm:ss.SSS zz} | {level: <8} | Line {line: >4} ({file}): {message}" +# logger.add(sys.stderr, level=log_level, format=log_format, colorize=True, backtrace=True, diagnose=True) +# logger.add("file.log", level=log_level, format=log_format, colorize=False, backtrace=True, diagnose=True) + + +def getUserMusicDetail(userId: int, nextIndex: int = 0, maxCount: int = 50) -> dict: + """获取用户的成绩的API""" + data = json.dumps( + {"userId": int(userId), "nextIndex": nextIndex, "maxCount": maxCount} + ) + return json.loads(apiSDGB(data, "GetUserMusicApi", userId)) + + +def getUserFullMusicDetail(userId: int): + """获取用户的全部成绩""" + currentUserMusicDetailList = [] + nextIndex: int | None = None # 初始化 nextIndex + while nextIndex != 0 or nextIndex is None: # 只要还有nextIndex就一直获取获取 + userMusicResponse = getUserMusicDetail(userId, nextIndex or 0) + nextIndex = userMusicResponse["nextIndex"] + logger.info(f"NextIndex: {nextIndex}") + # 处理已经没有 userMusicList 的情况 + if not userMusicResponse["userMusicList"]: + break + # 只要还有 userMusicList 就一直加进去,直到全部获取完毕 + for currentMusic in userMusicResponse["userMusicList"]: + for currentMusicDetail in currentMusic["userMusicDetailList"]: + if not currentMusicDetail["playCount"] > 0: + continue + currentUserMusicDetailList.append(currentMusicDetail) + return currentUserMusicDetailList + + +def parseUserFullMusicDetail(userFullMusicDetailList: list): + """解析用户的全部成绩,给出一个迫真人类可读 list 套 dict""" + musicDetailList = [] + for currentMusicDetail in userFullMusicDetailList: + musicDetailList.append( + { + "歌名": getMusicTitle(currentMusicDetail["musicId"]), + "难度": currentMusicDetail["level"], + "分数": currentMusicDetail["achievement"] / 10000, + "DX分数": currentMusicDetail["deluxscoreMax"], + } + ) + return musicDetailList + + +if __name__ == "__main__": + userId = testUid + userFullMusicDetailList = getUserFullMusicDetail(userId) + parsedUserFullMusicDetail = parseUserFullMusicDetail(userFullMusicDetailList) + logger.info(parsedUserFullMusicDetail) diff --git a/backend/HelperGetUserThing.py b/backend/HelperGetUserThing.py new file mode 100644 index 0000000..83fd5c2 --- /dev/null +++ b/backend/HelperGetUserThing.py @@ -0,0 +1,23 @@ +# 获取用户数据的 API 实现 +import rapidjson as json +from API_TitleServer import apiSDGB + + +def implGetUser_(thing: str, userId: int, noLog=False) -> dict: + """获取用户某些数据的 API 实现,返回 Dict""" + # 获取 Json String + result = apiGetUserThing(userId, thing, noLog) + # 转换为 Dict + userthingDict = json.loads(result) + # 返回 Dict + return userthingDict + + +def apiGetUserThing(userId: int, thing: str, noLog=False) -> str: + """获取用户数据的 API 请求器,返回 Json String""" + # 构建 Payload + data = json.dumps({"userId": userId}) + # 发送请求 + userthing_result = apiSDGB(data, "GetUser" + thing + "Api", userId, noLog) + # 返回响应 + return userthing_result diff --git a/backend/HelperLogInOut.py b/backend/HelperLogInOut.py new file mode 100644 index 0000000..3b274f7 --- /dev/null +++ b/backend/HelperLogInOut.py @@ -0,0 +1,83 @@ +# 登录·登出实现 +# 一般作为模块使用,但也可以作为 CLI 程序运行以强制登出账号。 + +import time +import random + +import rapidjson as json +from loguru import logger + +from API_TitleServer import apiSDGB +from Config import ( + clientId, + placeId, + regionId, +) +from MyConfig import testUid + + +def apiLogin(timestamp: int, userId: int, noLog: bool = False) -> dict: + """登录,返回 dict""" + data = json.dumps( + { + "userId": userId, + "accessCode": "", + "regionId": regionId, + "placeId": placeId, + "clientId": clientId, + "dateTime": timestamp, + "isContinue": False, + "genericFlag": 0, + } + ) + login_result = json.loads(apiSDGB(data, "UserLoginApi", userId, noLog)) + if not noLog: + logger.info("登录:结果:" + str(login_result)) + return login_result + + +def apiLogout(timestamp: int, userId: int, noLog: bool = False) -> dict: + """登出,返回 dict""" + data = json.dumps( + { + "userId": userId, + "accessCode": "", + "regionId": regionId, + "placeId": placeId, + "clientId": clientId, + "dateTime": timestamp, + "type": 1, + } + ) + logout_result = json.loads(apiSDGB(data, "UserLogoutApi", userId, noLog)) + if not noLog: + logger.info("登出:结果:" + str(logout_result)) + return logout_result + + +def generateTimestampLegacy() -> int: + """生成一个凑合用的时间戳""" + timestamp = int(time.time()) - 60 + logger.info(f"生成时间戳: {timestamp}") + return timestamp + + +def generateTimestamp() -> int: + """生成一个今天早上 10:00 随机偏移的时间戳""" + timestamp = int( + time.mktime( + time.strptime(time.strftime("%Y-%m-%d 10:00:00"), "%Y-%m-%d %H:%M:%S") + ) + ) + random.randint(-600, 600) + logger.info(f"生成时间戳: {timestamp}") + logger.info( + f"此时间戳对应的时间为: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp))}" + ) + return timestamp + + +if __name__ == "__main__": + print("强制登出 CLI") + uid = testUid + timestamp = generateTimestamp() + apiLogout(int(timestamp), int(uid)) diff --git a/backend/HelperMisc.py b/backend/HelperMisc.py new file mode 100644 index 0000000..f6a7cce --- /dev/null +++ b/backend/HelperMisc.py @@ -0,0 +1,302 @@ +# 杂项助手函数 +# 主要用于当作模块使用的时候的一些生活质量提升 +import rapidjson as json +from loguru import logger +from HelperGetUserThing import implGetUser_ +import unicodedata +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp + +from MyConfig import testUid + + +def numberToLetter(number): + """ + 将数字转换为字母(1-26 to A-Z) + """ + if 1 <= number <= 26: + return chr(number + 64) + else: + return None + + +def maimaiVersionToHumanReadable(romVersion: str, dataVersion: str) -> str: + try: + romVersionList = romVersion.split(".") + dataVersionList = dataVersion.split(".") + except Exception as e: + logger.warning(f"无法解析版本号: {romVersion} {dataVersion},错误:{e}") + return "无效版本号:无法解析" + try: + romVersionList = [int(i) for i in romVersionList] + dataVersionList = [int(i) for i in dataVersionList] + except Exception as e: + logger.warning(f"无法解析版本号: {romVersion} {dataVersion},错误:{e}") + return "无效版本号:无法解读数字" + finalVersionList = [] + finalVersionList.append(romVersionList[0]) + # quirk + minorVer = max(romVersionList[1], dataVersionList[1]) + if minorVer == 0: + finalVersionList.append("00") + else: + finalVersionList.append(minorVer) + finalVersionLetter = numberToLetter(max(romVersionList[2], dataVersionList[2])) + if finalVersionLetter: + finalVersionLetter = f"-{finalVersionLetter}" + else: + finalVersionLetter = "" + finalVersionList.append(finalVersionLetter) + + if int(finalVersionList[1]) < 30: + versionStringPrefix = "CH" + else: + versionStringPrefix = "CN" + + finalVersionString = f"{versionStringPrefix}{finalVersionList[0]}.{finalVersionList[1]}{finalVersionList[2]}" + return finalVersionString + + +levelIdDict = {"绿": 0, "黄": 1, "红": 2, "紫": 3, "白": 4, "宴": 5} + + +def getHalfWidthString(s): + """全角转半角,舞萌ID用""" + return unicodedata.normalize("NFKC", s) + + +def getHumanReadableLoginErrorCode(loginResult) -> str: + """解析登录结果并且给出中文的报错解释""" + match loginResult["returnCode"]: + case 1: + return False + case 100: + return "❌ 用户正在上机游玩,请下机后再试,或等待 15 分钟。" + case 102: + return "⚠️ 请在微信公众号内点击一次获取新的二维码,然后再试。" + case 103: + return "❌ 试图登录的账号 UID 无效,请检查账号是否正确。" + case _: + return "❌ 登录失败!这不应该发生,请反馈此问题。错误详情:" + loginResult + + +def checkTechnologyUseCount(userId: int) -> int: + """猜测账号是否用了科技,0没用过,其他为用过""" + userData1 = implGetUser_("Data", userId) + userData = userData1.get("userData", {}) + userRegion = implGetUser_("Region", userId) + userRegionList = userRegion.get("userRegionList", []) + + playCount = userData.get("playCount", 0) + allRegionPlayCount = 0 + for region in userRegionList: + allRegionPlayCount += region.get("playCount", 0) + logger.info( + f"用户 {userId} 的总游玩次数: {playCount}, 各地区游玩次数: {allRegionPlayCount}" + ) + # 计算全部的 Region 加起来的游玩次数是否和 playCount 对不上,对不上就是用了科技 + # 返回差值 + return playCount - allRegionPlayCount + + +def getFriendlyUserData(userId: int) -> str: + """生成一个(相对)友好的UserData的人话""" + userData1 = implGetUser_("Data", userId) + userData = userData1.get("userData", {}) + userRegion = implGetUser_("Region", userId) + banState = userData1.get("banState") + + result = f"用户: {getHalfWidthString(userData.get('userName', '未知'))}\n" + result += f"DX RATING: {userData.get('playerRating', '未知')} " + result += f"B35: {userData.get('playerOldRating', '未知')} " + result += f"B15: {userData.get('playerNewRating', '未知')}\n" + result += f"总游戏次数: {userData.get('playCount', '未知')} " + result += f"当前版本游戏次数: {userData.get('currentPlayCount', '未知')}\n" + result += f"最近登录时间: {userData.get('lastLoginDate')} " + result += f"最近登录版本: {maimaiVersionToHumanReadable(userData.get('lastRomVersion'), userData.get('lastDataVersion'))} " + result += f"最近登录地区: {userData.get('lastRegionName', '未知')}\n" + result += f"注册日期: {userData.get('firstPlayDate')} " + result += f"注册版本: {maimaiVersionToHumanReadable(userData.get('firstRomVersion'), userData.get('firstDataVersion'))}\n" + result += f"封号状态(banState): {banState}\n" + try: + logger.info(userRegion) + result += getHumanReadableRegionData(userRegion) + except Exception as e: + result += f"地区数据获取失败:{e}\n" + + return result + + +def getHumanReadableRegionData(userRegion: str) -> str: + """生成一个人类可读的地区数据""" + userRegionList = userRegion.get("userRegionList") + logger.info(userRegionList) + result = "" + for region in userRegionList: + regionName = WAHLAP_REGIONS.get(region["regionId"], "未知") + playCount = region["playCount"] + created = region["created"] + result += f"\n{regionName} 游玩次数: {playCount} 首次游玩: {created}" + return result + + +def getHumanReadablePreview(preview_json_content: str) -> str: + """简单,粗略地解释 Preview 的 Json String 为人话。""" + previewData = json.loads(preview_json_content) + userName = getHalfWidthString(previewData["userName"]) + playerRating = previewData["playerRating"] + finalString = f"用户名:{userName}\nDX RATING:{playerRating}\n" + return finalString + + +def getHumanReadableLoginBonusList(jsonString: str): + """生成一个人类可读的 Login Bonus 的列表""" + data = json.loads(jsonString) + + result = [] + for bonus in data["userLoginBonusList"]: + if not bonus["isComplete"]: # 过滤已经集满的 + line = f"BonusID {bonus['bonusId']} 已集 {bonus['point']} 点" + if bonus["isCurrent"]: # 如果是当前选中,追加标记 + line += "(当前选中)" + result.append(line) + + resultString = "" + for line in result: # 转成字符串 + resultString += line + "\n" + + return resultString + + +def getHumanReadableTicketList(jsonString: str): + """生成一个人类可读的 UserCharge 的列表""" + data = json.loads(jsonString) + + userId = data["userId"] + length = data["length"] + userChargeList = data["userChargeList"] + + result = f"UID: {userId} 票槽大小: {length} 所有记录:" + for currentItem in userChargeList: + chargeId = currentItem["chargeId"] + stock = currentItem["stock"] + purchaseDate = currentItem["purchaseDate"] + validDate = currentItem["validDate"] + + result += f"\nID: {chargeId} 持有: {stock}, 购买日期: {purchaseDate}, 有效期限: {validDate}" + + return result + + +def getHumanReadableUserData(userData) -> str: + """生成一个人类可读的 UserData 的数据(比较详细)""" + userId = userData.get("userId") + userData = userData.get("userData", {}) + banState = userData.get("banState") + logger.debug(userData) + + result = f"用户名: {userData.get('userName', '未知')} " + result += f"UID: {userId}\n" + result += f"当前 RATING: {userData.get('playerRating', '未知')} " + result += f"B35: {userData.get('playerOldRating', '未知')} " + result += f"B15: {userData.get('playerNewRating', '未知')} " + result += f"最高 RATING: {userData.get('highestRating', '未知')}\n" + result += f"级别段位: {userData.get('gradeRank', '未知')} " + result += f"段位认定: {userData.get('courseRank', '未知')} " + result += f"友人对战段位: {userData.get('classRank', '未知')}\n" + result += f"总游戏次数: {userData.get('playCount', '未知')} " + result += f"当前版本游戏次数: {userData.get('currentPlayCount', '未知')}\n" + result += f"总DX分: {userData.get('totalDeluxscore', '未知')} " + result += f"绿谱总DX分: {userData.get('totalBasicDeluxscore', '未知')} " + result += f"黄谱总DX分: {userData.get('totalAdvancedDeluxscore', '未知')} " + result += f"红谱总DX分: {userData.get('totalExpertDeluxscore', '未知')} " + result += f"紫谱总DX分: {userData.get('totalMasterDeluxscore', '未知')} " + result += f"白谱总DX分: {userData.get('totalReMasterDeluxscore', '未知')}\n" + result += f"总SYNC: {userData.get('totalSync', '未知')} " + result += f"绿谱总SYNC: {userData.get('totalBasicSync', '未知')} " + result += f"黄谱总SYNC: {userData.get('totalAdvancedSync', '未知')} " + result += f"红谱总SYNC: {userData.get('totalExpertSync', '未知')} " + result += f"紫谱总SYNC: {userData.get('totalMasterSync', '未知')} " + result += f"白谱总SYNC: {userData.get('totalReMasterSync', '未知')}\n" + result += f"总分: {userData.get('totalAchievement', '未知')} " + result += f"绿谱总分: {userData.get('totalBasicAchievement', '未知')} " + result += f"黄谱总分: {userData.get('totalAdvancedAchievement', '未知')} " + result += f"红谱总分: {userData.get('totalExpertAchievement', '未知')} " + result += f"紫谱总分: {userData.get('totalMasterAchievement', '未知')} " + result += f"白谱总分: {userData.get('totalReMasterAchievement', '未知')}\n" + result += f"活动事件日期: {userData.get('eventWatchedDate')}\n" + result += f"最后 ROM 版本: {userData.get('lastRomVersion', '未知')}\n" + result += f"最后数据版本: {userData.get('lastDataVersion', '未知')}\n" + result += f"最后登录时间: {userData.get('lastLoginDate')}\n" + result += f"最后游戏时间: {userData.get('lastPlayDate')}\n" + result += f"最后双人登录时间: {userData.get('lastPairLoginDate')}\n" + result += f"最后免费游戏时间: {userData.get('lastTrialPlayDate')}\n" + result += f"最后游戏花费: {userData.get('lastPlayCredit', '未知')}\n" + result += f"最后地区 ID: {userData.get('lastRegionId', '未知')}\n" + result += f"最后地区名称: {userData.get('lastRegionName', '未知')}\n" + result += f"最后选择功能票: {userData.get('lastSelectTicket', '未知')}\n" + result += f"最后一次段位认定: {userData.get('lastSelectCourse', '未知')}\n" + result += f"最后一次 Course 计数: {userData.get('lastCountCourse', '未知')}\n" + result += f"注册 ROM 版本: {userData.get('firstRomVersion', '未知')}\n" + result += f"注册数据版本: {userData.get('firstDataVersion', '未知')}\n" + result += f"注册日期: {userData.get('firstPlayDate')}\n" + result += f"总觉醒: {userData.get('totalAwake', '未知')}\n" + result += f"签到日期: {userData.get('dailyCourseBonusDate')}\n" + result += f"跑图存储距离: {userData.get('mapStock', '未知')}m\n" + result += f"封号状态: {banState}\n" + return result + + +WAHLAP_REGIONS = { + 1: "北京", + 2: "重庆", + 3: "上海", + 4: "天津", + 5: "安徽", + 6: "福建", + 7: "甘肃", + 8: "广东", + 9: "贵州", + 10: "海南", + 11: "河北", + 12: "黑龙江", + 13: "河南", + 14: "湖北", + 15: "湖南", + 16: "江苏", + 17: "江西", + 18: "吉林", + 19: "辽宁", + 20: "青海", + 21: "陕西", + 22: "山东", + 23: "山西", + 24: "四川", + 25: "(未知25)", + 26: "云南", + 27: "浙江", + 28: "广西", + 29: "内蒙古", + 30: "宁夏", + 31: "新疆", + 32: "西藏", +} + +if __name__ == "__main__": + # test version string convert + print(maimaiVersionToHumanReadable("1.20.0", "1.0.0")) + print(maimaiVersionToHumanReadable("1.41.00", "1.40.11")) + print(maimaiVersionToHumanReadable("1.00.00", "1.00.00")) + + userId = testUid + currentLoginTimestamp = generateTimestamp() + loginResult = apiLogin(currentLoginTimestamp, userId) + + if loginResult["returnCode"] != 1: + logger.info("登录失败") + exit() + try: + logger.info(checkTechnologyUseCount(userId)) + # logger.info(apiQueryTicket(userId)) + finally: + logger.info(apiLogout(currentLoginTimestamp, userId)) + # logger.warning("Error") diff --git a/backend/HelperMusicDB.py b/backend/HelperMusicDB.py new file mode 100644 index 0000000..d65f284 --- /dev/null +++ b/backend/HelperMusicDB.py @@ -0,0 +1,14 @@ +from MusicDB import musicDB +from loguru import logger + + +def getMusicTitle(musicId: int) -> str: + """从数据库获取音乐的标题""" + # logger.debug(f"查询歌名: {musicId}") + musicInfo = musicDB.get(musicId) + if not musicInfo: + logger.warning(f"数据库里未找到此歌曲: {musicId}") + return "R_ERR_MUSIC_ID_NOT_IN_DATABASE" + musicName = musicInfo.get("name") + # logger.debug(f"成功查询到歌名: {musicName}") + return musicName diff --git a/backend/HelperUnlockThing.py b/backend/HelperUnlockThing.py new file mode 100644 index 0000000..10e3560 --- /dev/null +++ b/backend/HelperUnlockThing.py @@ -0,0 +1,97 @@ +# 解锁东西的一个通用的助手,不可独立使用 +from HelperFullPlay import implFullPlayAction + + +def implUnlockThing( + newUserItemList, userId: int, currentLoginTimestamp: int, currentLoginResult +) -> str: + musicData = { + "musicId": 11538, # Amber Chronicle + "level": 0, + "playCount": 1, + "achievement": 0, + "comboStatus": 0, + "syncStatus": 0, + "deluxscoreMax": 0, + "scoreRank": 0, + "extNum1": 0, + } + userAllPatches = { + "upsertUserAll": { + "userMusicDetailList": [musicData], + "isNewMusicDetailList": "1", + "userItemList": newUserItemList, + "isNewItemList": "1" * len(newUserItemList), + } + } + result = implFullPlayAction( + userId, currentLoginTimestamp, currentLoginResult, musicData, userAllPatches + ) + return result + + +itemKindDict = { + "PLATE": 1, # 姓名框 + "TITLE": 2, # 称号 + "ICON": 3, # 头像 + "MUSIC": 5, # 歌 + "MUSIC_MASTER": 6, # 紫谱 + "MUSIC_RE_MASTER": 7, # 白谱 + "CHARACTER": 9, # 旅行伙伴 + "PARTNER": 10, # 搭档 + "FRAME": 11, # 背景板 + "TICKET": 12, # 功能票 + # "PRESENT": 4, # ? + # "MUSIC_STRONG": 8, # ? +} + +itemKindzhCNDict = { + "姓名框": "PLATE", + "称号": "TITLE", + "头像": "ICON", + "歌": "MUSIC", + "紫谱": "MUSIC_MASTER", + "白谱": "MUSIC_RE_MASTER", + "旅行伙伴": "CHARACTER", + "搭档": "PARTNER", + "背景板": "FRAME", + "功能票": "TICKET", + # "礼物": "PRESENT", + # "STRONG": "MUSIC_STRONG", +} + +# Splash = 2020 +# UNiVERSE = 2022 +# FESTiVAL = 2023 +# BUDDiES = 2024 +# PRiSM = 2025 + +partnerList = { + "1": "迪拉熊", + "17": "青柠熊&柠檬熊", + "29": "青柠熊&柠檬熊(2024)", + "11": "乙姫", + "18": "乙姫(Splash)", + "28": "乙姫(2024)", + "12": "拉兹", + "23": "拉兹(2023)", + "30": "拉兹 (BUDDiES)", + "13": "雪纺", + "24": "雪纺(2023)", + "14": "莎露朵", + "25": "莎露朵(2023)", + "31": "莎露朵 (PRiSM)", + "15": "夏玛", + "19": "夏玛(UNiVERSE)", + "16": "咪璐库", + "21": "小咪璐库", + "32": "咪璐库 (PRiSM)", + "20": "咪璐库(UNiVERSE)", + "22": "百合咲美香", + "26": "黒姫", + "27": "俊达萌", + "33": "超天酱", +} + +for id, partner in partnerList.items(): + print() \ No newline at end of file diff --git a/backend/HelperUploadUserPlayLog.py b/backend/HelperUploadUserPlayLog.py new file mode 100644 index 0000000..44dadb5 --- /dev/null +++ b/backend/HelperUploadUserPlayLog.py @@ -0,0 +1,153 @@ +# 上传一个占位用的游玩记录的 API 实现 + +import rapidjson as json +import pytz +import time +import random +from datetime import datetime +from loguru import logger + +from API_TitleServer import apiSDGB +from Config import ( + placeId, + placeName, +) + + +def apiUploadUserPlaylog( + userId: int, musicDataToBeUploaded, currentUserData2, loginId: int +) -> str: + """ + 上传一个 UserPlayLog。 + 注意:成绩为随机的空成绩,只用作占位 + 返回 Json String。""" + + # 构建一个 PlayLog + data = json.dumps( + { + "userId": int(userId), + "userPlaylogList": [ + { + "userId": 0, + "orderId": 0, + "playlogId": loginId, + "version": 1051000, + "placeId": placeId, + "placeName": placeName, + "loginDate": int(time.time()), # 似乎和登录timestamp不同 + "playDate": datetime.now(pytz.timezone("Asia/Shanghai")).strftime( + "%Y-%m-%d" + ), + "userPlayDate": datetime.now( + pytz.timezone("Asia/Shanghai") + ).strftime("%Y-%m-%d %H:%M:%S") + + ".0", + "type": 0, + "musicId": int(musicDataToBeUploaded["musicId"]), + "level": int(musicDataToBeUploaded["level"]), + "trackNo": 1, + "vsMode": 0, + "vsUserName": "", + "vsStatus": 0, + "vsUserRating": 0, + "vsUserAchievement": 0, + "vsUserGradeRank": 0, + "vsRank": 0, + "playerNum": 1, + "playedUserId1": 0, + "playedUserName1": "", + "playedMusicLevel1": 0, + "playedUserId2": 0, + "playedUserName2": "", + "playedMusicLevel2": 0, + "playedUserId3": 0, + "playedUserName3": "", + "playedMusicLevel3": 0, + "characterId1": currentUserData2["charaSlot"][0], + "characterLevel1": random.randint(1000, 6500), + "characterAwakening1": 5, + "characterId2": currentUserData2["charaSlot"][1], + "characterLevel2": random.randint(1000, 6500), + "characterAwakening2": 5, + "characterId3": currentUserData2["charaSlot"][2], + "characterLevel3": random.randint(1000, 6500), + "characterAwakening3": 5, + "characterId4": currentUserData2["charaSlot"][3], + "characterLevel4": random.randint(1000, 6500), + "characterAwakening4": 5, + "characterId5": currentUserData2["charaSlot"][4], + "characterLevel5": random.randint(1000, 6500), + "characterAwakening5": 5, + "achievement": int(musicDataToBeUploaded["achievement"]), + "deluxscore": int(musicDataToBeUploaded["deluxscoreMax"]), + "scoreRank": int(musicDataToBeUploaded["scoreRank"]), + "maxCombo": 0, + "totalCombo": random.randint(700, 900), + "maxSync": 0, + "totalSync": 0, + "tapCriticalPerfect": 0, + "tapPerfect": 0, + "tapGreat": 0, + "tapGood": 0, + "tapMiss": random.randint(1, 10), + "holdCriticalPerfect": 0, + "holdPerfect": 0, + "holdGreat": 0, + "holdGood": 0, + "holdMiss": random.randint(1, 15), + "slideCriticalPerfect": 0, + "slidePerfect": 0, + "slideGreat": 0, + "slideGood": 0, + "slideMiss": random.randint(1, 15), + "touchCriticalPerfect": 0, + "touchPerfect": 0, + "touchGreat": 0, + "touchGood": 0, + "touchMiss": random.randint(1, 15), + "breakCriticalPerfect": 0, + "breakPerfect": 0, + "breakGreat": 0, + "breakGood": 0, + "breakMiss": random.randint(1, 15), + "isTap": True, + "isHold": True, + "isSlide": True, + "isTouch": True, + "isBreak": True, + "isCriticalDisp": True, + "isFastLateDisp": True, + "fastCount": 0, + "lateCount": 0, + "isAchieveNewRecord": True, + "isDeluxscoreNewRecord": True, + "comboStatus": 0, + "syncStatus": 0, + "isClear": False, + "beforeRating": currentUserData2["playerRating"], + "afterRating": currentUserData2["playerRating"], + "beforeGrade": 0, + "afterGrade": 0, + "afterGradeRank": 1, + "beforeDeluxRating": currentUserData2["playerRating"], + "afterDeluxRating": currentUserData2["playerRating"], + "isPlayTutorial": False, + "isEventMode": False, + "isFreedomMode": False, + "playMode": 0, + "isNewFree": False, + "trialPlayAchievement": -1, + "extNum1": 0, + "extNum2": 0, + "extNum4": 3020, + "extBool1": False, + "extBool2": False, + } + ], + } + ) + # 发送请求 + result = apiSDGB(data, "UploadUserPlaylogListApi", userId) + logger.info("上传游玩记录:结果:" + str(result)) + # 返回响应 + return result diff --git a/backend/HelperUserAll.py b/backend/HelperUserAll.py new file mode 100644 index 0000000..bfce643 --- /dev/null +++ b/backend/HelperUserAll.py @@ -0,0 +1,270 @@ +# UserAll 有关的一些辅助函数 + +import pytz +from datetime import datetime +from loguru import logger + +from HelperGetUserThing import implGetUser_ +from HelperGetUserMusicDetail import getUserMusicDetail + +from Config import ( + clientId, + placeName, + placeId, + regionId, + regionName, +) + + +def isNewMusicType(userId, musicId, level) -> str: + """判断这首 musicId 在 isNewMusicDetailList 应该填什么 + 0: Edit + 1: Insert + 一次只能处理一首歌,所以返回值是 str + + 未完工,仅供测试 + """ + userMusicDetailList = getUserMusicDetail(userId, musicId, 1)["userMusicList"][0][ + "userMusicDetailList" + ] + logger.info(userMusicDetailList) + try: + if ( + userMusicDetailList[0]["musicId"] == musicId + and userMusicDetailList[0]["level"] == level + ): + logger.info(f"We think {musicId} Level {level} should use EDIT.") + return "0" + except Exception: + return "1" + + +def generateFullUserAll( + userId, + currentLoginResult, + currentLoginTimestamp, + currentUserData2, + currentPlaySpecial, +): + """从服务器取得必要的数据并构建一个比较完整的 UserAll""" + + # 先构建一个基础 UserAll + currentUserAll = generateUserAllData( + userId, + currentLoginResult, + currentLoginTimestamp, + currentUserData2, + currentPlaySpecial, + ) + + # 然后从服务器取得必要的数据 + currentUserExtend = implGetUser_("Extend", userId, True) + currentUserOption = implGetUser_("Option", userId, True) + currentUserRating = implGetUser_("Rating", userId, True) + currentUserActivity = implGetUser_("Activity", userId, True) + currentUserCharge = implGetUser_("Charge", userId, True) + currentUserMissionData = implGetUser_("MissionData", userId, True) + + # 把这些数据都追加进去 + currentUserAll["upsertUserAll"]["userExtend"] = [currentUserExtend["userExtend"]] + currentUserAll["upsertUserAll"]["userOption"] = [currentUserOption["userOption"]] + currentUserAll["upsertUserAll"]["userRatingList"] = [ + currentUserRating["userRating"] + ] + currentUserAll["upsertUserAll"]["userActivityList"] = [ + currentUserActivity["userActivity"] + ] + currentUserAll["upsertUserAll"]["userChargeList"] = currentUserCharge[ + "userChargeList" + ] + currentUserAll["upsertUserAll"]["userWeeklyData"] = currentUserMissionData[ + "userWeeklyData" + ] + + # 完事 + return currentUserAll + + +def generateUserAllData( + userId, + currentLoginResult, + currentLoginTimestamp, + currentUserData2, + currentPlaySpecial, +): + """构建一个非常基础的 UserAll 数据,必须手动填充一些数据""" + + data = { + "userId": userId, + "playlogId": currentLoginResult["loginId"], + "isEventMode": False, + "isFreePlay": False, + "upsertUserAll": { + "userData": [ + { + "accessCode": "", + "userName": currentUserData2["userName"], + "isNetMember": 1, + "point": currentUserData2["point"], + "totalPoint": currentUserData2["totalPoint"], + "iconId": currentUserData2["iconId"], + "plateId": currentUserData2["plateId"], + "titleId": currentUserData2["titleId"], + "partnerId": currentUserData2["partnerId"], + "frameId": currentUserData2["frameId"], + "selectMapId": currentUserData2["selectMapId"], + "totalAwake": currentUserData2["totalAwake"], + "gradeRating": currentUserData2["gradeRating"], + "musicRating": currentUserData2["musicRating"], + "playerRating": currentUserData2["playerRating"], + "highestRating": currentUserData2["highestRating"], + "gradeRank": currentUserData2["gradeRank"], + "classRank": currentUserData2["classRank"], + "courseRank": currentUserData2["courseRank"], + "charaSlot": currentUserData2["charaSlot"], + "charaLockSlot": currentUserData2["charaLockSlot"], + "contentBit": currentUserData2["contentBit"], + "playCount": currentUserData2["playCount"], + "currentPlayCount": currentUserData2["currentPlayCount"], + "renameCredit": 0, + "mapStock": currentUserData2["mapStock"], + "eventWatchedDate": currentUserData2["eventWatchedDate"], + "lastGameId": "SDGB", + "lastRomVersion": currentUserData2["lastRomVersion"], + "lastDataVersion": currentUserData2["lastDataVersion"], + # "lastLoginDate": currentLoginResult['lastLoginDate'], # sb + "lastLoginDate": currentUserData2["lastLoginDate"], # 等待测试 + "lastPlayDate": datetime.now( + pytz.timezone("Asia/Shanghai") + ).strftime("%Y-%m-%d %H:%M:%S") + + ".0", + "lastPlayCredit": 1, + "lastPlayMode": 0, + "lastPlaceId": placeId, + "lastPlaceName": placeName, + "lastAllNetId": 0, + "lastRegionId": regionId, + "lastRegionName": regionName, + "lastClientId": clientId, + "lastCountryCode": "CHN", + "lastSelectEMoney": 0, + "lastSelectTicket": 0, + "lastSelectCourse": currentUserData2["lastSelectCourse"], + "lastCountCourse": 0, + "firstGameId": "SDGB", + "firstRomVersion": currentUserData2["firstRomVersion"], + "firstDataVersion": currentUserData2["firstDataVersion"], + "firstPlayDate": currentUserData2["firstPlayDate"], + "compatibleCmVersion": currentUserData2["compatibleCmVersion"], + "dailyBonusDate": currentUserData2["dailyBonusDate"], + "dailyCourseBonusDate": currentUserData2["dailyCourseBonusDate"], + "lastPairLoginDate": currentUserData2["lastPairLoginDate"], + "lastTrialPlayDate": currentUserData2["lastTrialPlayDate"], + "playVsCount": 0, + "playSyncCount": 0, + "winCount": 0, + "helpCount": 0, + "comboCount": 0, + "totalDeluxscore": currentUserData2["totalDeluxscore"], + "totalBasicDeluxscore": currentUserData2["totalBasicDeluxscore"], + "totalAdvancedDeluxscore": currentUserData2[ + "totalAdvancedDeluxscore" + ], + "totalExpertDeluxscore": currentUserData2["totalExpertDeluxscore"], + "totalMasterDeluxscore": currentUserData2["totalMasterDeluxscore"], + "totalReMasterDeluxscore": currentUserData2[ + "totalReMasterDeluxscore" + ], + "totalSync": currentUserData2["totalSync"], + "totalBasicSync": currentUserData2["totalBasicSync"], + "totalAdvancedSync": currentUserData2["totalAdvancedSync"], + "totalExpertSync": currentUserData2["totalExpertSync"], + "totalMasterSync": currentUserData2["totalMasterSync"], + "totalReMasterSync": currentUserData2["totalReMasterSync"], + "totalAchievement": currentUserData2["totalAchievement"], + "totalBasicAchievement": currentUserData2["totalBasicAchievement"], + "totalAdvancedAchievement": currentUserData2[ + "totalAdvancedAchievement" + ], + "totalExpertAchievement": currentUserData2[ + "totalExpertAchievement" + ], + "totalMasterAchievement": currentUserData2[ + "totalMasterAchievement" + ], + "totalReMasterAchievement": currentUserData2[ + "totalReMasterAchievement" + ], + "playerOldRating": currentUserData2["playerOldRating"], + "playerNewRating": currentUserData2["playerNewRating"], + "banState": 0, + "friendRegistSkip": currentUserData2["friendRegistSkip"], + "dateTime": currentLoginTimestamp, + } + ], + "userExtend": [], # 需要填上 + "userOption": [], # 需要填上 + "userGhost": [], + "userCharacterList": [], + "userMapList": [], + "userLoginBonusList": [], + "userRatingList": [], # 需要填上 + "userItemList": [], # 可选,但经常要填上 + "userMusicDetailList": [], # 需要填上 + "userCourseList": [], + "userFriendSeasonRankingList": [], + "userChargeList": [], # 需要填上 + "userFavoriteList": [], + "userActivityList": [], # 需要填上 + "userMissionDataList": [], + "userWeeklyData": [], # 应该需要填上 + "userGamePlaylogList": [ + { + "playlogId": currentLoginResult["loginId"], + "version": "1.51.00", + "playDate": datetime.now(pytz.timezone("Asia/Shanghai")).strftime( + "%Y-%m-%d %H:%M:%S" + ) + + ".0", + "playMode": 0, + "useTicketId": -1, + "playCredit": 1, + "playTrack": 1, + "clientId": clientId, + "isPlayTutorial": False, + "isEventMode": False, + "isNewFree": False, + "playCount": currentUserData2["playCount"], + "playSpecial": currentPlaySpecial, + "playOtherUserId": 0, + } + ], + "user2pPlaylog": { + "userId1": 0, + "userId2": 0, + "userName1": "", + "userName2": "", + "regionId": 0, + "placeId": 0, + "user2pPlaylogDetailList": [], + }, + "userIntimateList": [], + "userShopItemStockList": [], + "userGetPointList": [], + "userTradeItemList": [], + "userFavoritemusicList": [], + "userKaleidxScopeList": [], + "isNewCharacterList": "", + "isNewMapList": "", + "isNewLoginBonusList": "", + "isNewItemList": "", + "isNewMusicDetailList": "", # 可选但经常要填上 + "isNewCourseList": "0", + "isNewFavoriteList": "", + "isNewFriendSeasonRankingList": "", + "isNewUserIntimateList": "", + "isNewFavoritemusicList": "", + "isNewKaleidxScopeList": "", + }, + } + return data diff --git a/backend/MusicDB.py b/backend/MusicDB.py new file mode 100644 index 0000000..24aec0e --- /dev/null +++ b/backend/MusicDB.py @@ -0,0 +1,23 @@ +import rapidjson as json +from Config import * +from typing import Dict, Union + +# 定义音乐数据库的类型注解 +MusicDBType = Dict[int, Dict[str, Union[int, str]]] + +# 将 '__all__' 用于模块导出声明 +__all__ = ["musicDB"] + +# 读取并解析 JSON 文件 +try: + with open(musicDBPath, "r", encoding="utf-8") as f: + data = json.load(f) +except FileNotFoundError: + try: + with open(musicDBPathFallback, "r", encoding="utf-8") as f: + data = json.load(f) + except Exception: + raise FileNotFoundError("musicDB.json 文件不存在!") + +# 将 JSON 数据转换为指定格式的字典 +musicDB: MusicDBType = {int(k): v for k, v in data.items()} diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..645482b --- /dev/null +++ b/backend/README.md @@ -0,0 +1,2 @@ +# maimai DX API +API of maimai DX CN(SDGB). diff --git a/backend/Standalone/DecryptAuthLiteTraffic.py b/backend/Standalone/DecryptAuthLiteTraffic.py new file mode 100644 index 0000000..88967ab --- /dev/null +++ b/backend/Standalone/DecryptAuthLiteTraffic.py @@ -0,0 +1,33 @@ +# 解密国服 Auth Lite 的包 +# 仅测试过 PowerOn 请求,其他包不确定是否适用 +# 仅适用国服 DX2024,其他未测试 + +# 完全 Standalone,不依赖于其他文件 + +import base64 +from Crypto.Cipher import AES +from urllib.parse import unquote +from Crypto.Util.Padding import unpad + +# 密钥从 HDD 提取 +LITE_AUTH_KEY = bytes([47, 63, 106, 111, 43, 34, 76, 38, 92, 67, 114, 57, 40, 61, 107, 71]) +LITE_AUTH_IV = bytes.fromhex('00000000000000000000000000000000') + +# 填入你抓包得到的想解密的数据的 base64 编码 +base64String = "N0PyrjawH/7qA8A28y++3txHDsKuAs5+nib751QiNlJYigvwldPaG7xd0WYXvgqlWY16JIy38GQ8+M4ttaWRNfpWy9l29pC2h2abd4VGhIeWGLbOjc2Bthqhibui76vi4dW+05TsPiyXbOsqHFzScvdByKUtZUobZgrnr/WW+YqRIUdw/ZHBmKBY81JivnVH9AkEyCCP9xubYMjDqi65WhDpcrdMk5nUjHq/O7R1eXr12Es9gXDUruy/H4M7eMt+4kFSDCGpLSFwAEDhba6rpOz0n588nfvXXFlZ+a3ZsZSBYAJPBZ795Ck8ZDIYnEMWMV5nk6qPc2HiBF9ZZw88FlATGC8NqsTSjGX6JJXWDApUaSF5obXMu4LTmMMr0KDt2fQ6VQPkLnTgJ6tsJv1iAQvtcZ9ymn3I4S0XWXGmEq8r7XE7D+pnSJyjUn7bSXb6HOzCQtQc9XYmIbylS2sNkiDXywrxVgmiAXc4Ta8M9aNUb+81QrKj6qqC06DzdYQNBFRxb78X3nGYECEmKBsxIOR7Mf/ZqWYtA28Ob9H5CCUmjLvUaoQ+htJMHfQ9fvvevfu+FxtlJXfw+3UQDiQ1xvSZe2NMCgkLOuwqZ5/5PyoAV9MKzRXT4hBzDoiAIt7bzOH9JcNJkjUtLAjXbnwN6M6zUKpgMK4WYeCwUffNy21GbLVtfIxZZbVhK8A6Ni7j" + +def auth_lite_decrypt(ciphertext: bytes) -> str: + # 解密并去除填充 + cipher = AES.new(LITE_AUTH_KEY, AES.MODE_CBC, LITE_AUTH_IV) + decrypted_data = unpad(cipher.decrypt(ciphertext), AES.block_size) + # 提取内容并解码 + content = decrypted_data[16:] # 去除头部的16字节 + return content.decode('utf-8').strip() + +# 解码 base64 +decodedData = base64.b64decode(base64String) +# 解密数据 +decryptedData = auth_lite_decrypt(decodedData) +print(decryptedData) +# 解码 URL 编码 +print(unquote(decryptedData)) diff --git a/backend/Standalone/DecryptTitleServerTraffic.py b/backend/Standalone/DecryptTitleServerTraffic.py new file mode 100644 index 0000000..76df416 --- /dev/null +++ b/backend/Standalone/DecryptTitleServerTraffic.py @@ -0,0 +1,74 @@ +# 解密从 HDD 抓包得到的数据 +# 兼容 PRiSM 和 CN 2024 + +# 完全 Standalone,不依赖于其他文件 + +import base64 +import zlib +from Crypto.Cipher import AES +from Crypto.Util.Padding import unpad, pad + +# CN 2024 +AES_KEY_SDGB_1_40 = "n7bx6:@Fg_:2;5E89Phy7AyIcpxEQ:R@" +AES_IV_SDGB_1_40 = ";;KjR1C3hgB1ovXa" + +# 国际服 PRiSM +AES_KEY_SDGA_1_50 = "A;mv5YUpHBK3YxTy5KB^[;5]C2AL50Bq" +AES_IV_SDGA_1_50 = "9FM:sd9xA91X14v]" + +AES_KEY_SDGB_1_50 = "a>32bVP7v<63BVLkY[xM>daZ1s9MBP bytes: + cipher = AES.new(self.key, self.mode, self.iv) + content_padded = pad(content.encode(), AES.block_size) + encrypted_bytes = cipher.encrypt(content_padded) + return encrypted_bytes + # 解密 + def decrypt(self, encrypted_content: bytes) -> str: + cipher = AES.new(self.key, self.mode, self.iv) + decrypted_padded = cipher.decrypt(encrypted_content) + decrypted = unpad(decrypted_padded, AES.block_size) + return decrypted + +def main_sdga(): + # 填入你的想解密的数据的 base64 编码 + base64_encoded_data = "KSGm2qo7qVHz1wrK15PckYC5/kLjKcTtEXOgHeHt1Xn6DPdo3pltoPLADHpe8+Wq" + + aes = AESPKCS7(AES_KEY_SDGA_1_50, AES_IV_SDGA_1_50) + + # 首先解码 base64 + decodedData = base64.b64decode(base64_encoded_data) + # 然后解密数据,PRiSM 是先压缩再加密(which is 正确做法) + decryptedData = aes.decrypt(decodedData) + # 解压数据 + decompressedData = zlib.decompress(decryptedData) + + print(str(decompressedData)) + +def main_sdgb140(): + # 填入你的想解密的数据的 base64 编码 + base64_encoded_data = "eJyrTVvpuGwCR32OdodwtVXZ7/Ofmfhin7k/K61q3XNoad1rAPGwECU=" + + aes = AESPKCS7(AES_KEY_SDGB_1_40, AES_IV_SDGB_1_40) + + # 首先解码 base64 + decodedData = base64.b64decode(base64_encoded_data) + # 然后解压数据,CN 2024 是加密后再压缩(纯傻逼 + decompressedData = zlib.decompress(decodedData) + # 最后解密数据 + decryptedData = aes.decrypt(decompressedData) + + print(str(decryptedData)) + + +if __name__ == "__main__": + main_sdga() + main_sdgb140() diff --git a/backend/Standalone/DummyAimeDBServer.py b/backend/Standalone/DummyAimeDBServer.py new file mode 100644 index 0000000..78eaff0 --- /dev/null +++ b/backend/Standalone/DummyAimeDBServer.py @@ -0,0 +1,78 @@ +# 舞萌DX AimeDB 服务器模拟实现 +# 适用于舞萌DX 2024 +# 理论可用于 HDD 登号等(这种情况下自行修改 hosts + +# SGWCMAID111111111111AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + +## 配置 +# 0 返回本地生成的假结果 +# 1 原样返回官方服务器的结果 +useOfficialServer = 0 + +from loguru import logger +# 解析命令行参数,作为用户 ID +import argparse +try: + parser = argparse.ArgumentParser(description="舞萌DX AimeDB 服务器模拟实现") + #parser.add_argument("userId", type=int, help="用户 ID") + #args = parser.parse_args() + #DUMMY_USER_ID = args.userId + raise Exception("未传入用户 ID") +except Exception as e: + logger.warning(f"{e},未传入用户 ID,使用默认值") + DUMMY_USER_ID = 1 + +from fastapi import ( + FastAPI, + Request +) +from fastapi.responses import ( + PlainTextResponse, + JSONResponse +) +import rapidjson as json +import uvicorn +from loguru import logger + +# 将当前目录的父目录加入到 sys.path 中 +import sys +from pathlib import Path +current_dir = Path(__file__).resolve().parent +parent_dir = current_dir.parent +sys.path.append(str(parent_dir)) + +from API_AimeDB import implAimeDB, calcSEGAAimeDBAuthKey + +app = FastAPI() + +@app.post('/qrcode/api/alive_check') +async def qrcode_alive_check_api(): + return PlainTextResponse('alive') + +@app.post('/wc_aime/api/alive_check') +async def wc_aime_alive_check_api(): + return PlainTextResponse('alive') + +@app.post('/wc_aime/api/get_data') +async def get_data_dummy_api(request: Request): + gotRequest = json.loads((await request.body()).decode()) + + if useOfficialServer == 0: + fakeTimestamp = str(int(gotRequest['timestamp'])+3) + currentResponse = { + 'errorID': 0, + 'key': calcSEGAAimeDBAuthKey(str(DUMMY_USER_ID), fakeTimestamp), + 'timestamp': fakeTimestamp, + 'userID': DUMMY_USER_ID + } + logger.info(f"返回假结果: {currentResponse}") + return JSONResponse(currentResponse) + elif useOfficialServer == 1: + # 发给真正的 AimeDB + realAimeDBResponse = implAimeDB(gotRequest['qrCode'], True) + return JSONResponse(json.loads(realAimeDBResponse)) + else: + pass + +if __name__ == '__main__': + uvicorn.run(app, host="0.0.0.0", port=80) diff --git a/backend/Standalone/DummyAuthLiteServer.py b/backend/Standalone/DummyAuthLiteServer.py new file mode 100644 index 0000000..4cdcf00 --- /dev/null +++ b/backend/Standalone/DummyAuthLiteServer.py @@ -0,0 +1,69 @@ +# 舞萌DX Auth-Lite 服务器模拟实现 +# 仅实现了 /net/initialize 接口,用来处理 PowerOn + +# NOT FINISHED: ONLY A DUMMY IMPLEMENTATION FOR TESTING +# Contact me if you have more information about this + +from fastapi import ( + FastAPI, + Request +) +from fastapi.responses import ( + HTMLResponse +) +import uvicorn +import httpx +from loguru import logger +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad + +# 从 HDD 提取 +LITE_AUTH_KEY = bytes([47, 63, 106, 111, 43, 34, 76, 38, 92, 67, 114, 57, 40, 61, 107, 71]) +LITE_AUTH_IV = bytes.fromhex('00000000000000000000000000000000') + +def auth_lite_encrypt(plaintext: str) -> bytes: + # 构造数据:16字节头 + 16字节0前缀 + 明文 + header = bytes(16) + content = bytes(16) + plaintext.encode('utf-8') + data = header + content + # 填充并加密 + padded_data = pad(data, AES.block_size) + cipher = AES.new(LITE_AUTH_KEY, AES.MODE_CBC, LITE_AUTH_IV) + return cipher.encrypt(padded_data) + +def auth_lite_decrypt(ciphertext: bytes) -> str: + # 解密并去除填充 + cipher = AES.new(LITE_AUTH_KEY, AES.MODE_CBC, LITE_AUTH_IV) + decrypted_data = unpad(cipher.decrypt(ciphertext), AES.block_size) + # 提取内容并解码 + content = decrypted_data[16:] # 去除头部的16字节 + return content.decode('utf-8').strip() + +def apiOfficialServer(encryptedString: str): + url = "http://at.sys-allnet.cn/net/initialize" + headers = { + "Content-Type": "application/x-www-form-urlencoded", + "User-Agent": "SDGB;Windows/Lite" + } + data = encryptedString + response = httpx.post(url, headers=headers, data=data) + return response.content + +app = FastAPI() + +USE_OFFICIAL_SERVER = 1 + +@app.post('/net/initialize') +async def get_data_dummy_api(request: Request): + gotRequest = (await request.body()) + if USE_OFFICIAL_SERVER == 1: + decrypted = auth_lite_decrypt(gotRequest) + officialResponse = apiOfficialServer(auth_lite_encrypt(decrypted)) + logger.info(auth_lite_decrypt(officialResponse)) + return HTMLResponse(officialResponse) + else: + # todo + pass + +if __name__ == '__main__': + uvicorn.run(app, host="0.0.0.0", port=80) diff --git a/backend/Standalone/Script_GenerateLoginBonusDB.py b/backend/Standalone/Script_GenerateLoginBonusDB.py new file mode 100644 index 0000000..33066ef --- /dev/null +++ b/backend/Standalone/Script_GenerateLoginBonusDB.py @@ -0,0 +1,6 @@ +# Never Gonna Give You Up +# Never Gonna Let You Down +# Never Gonna Run Around And Desert You +# Never Gonna Make You Cry +# Never Gonna Say Goodbye +# Never Gonna Tell A Lie And Hurt You diff --git a/backend/Standalone/Script_GenerateMusicDB.py b/backend/Standalone/Script_GenerateMusicDB.py new file mode 100644 index 0000000..557b55f --- /dev/null +++ b/backend/Standalone/Script_GenerateMusicDB.py @@ -0,0 +1,51 @@ +# 感谢伟大的 Diving-Fish 让我被迫直面恐惧 +import xml.dom.minidom as minidom +from pathlib import Path +import rapidjson as json +from loguru import logger + +def makeMusicDBJson(): + ''' + 从 HDD 的文件来生成 music_db.json + 推荐的是如果要国服用 那就用国际服的文件来生成 + 免得国服每次更新还要重新生成太麻烦 + ''' + # 记得改 + A000_DIR = Path('H:\PRiSM\Package\Sinmai_Data\StreamingAssets\A000') + OPTION_DIR = Path('H:\PRiSM\Package\Sinmai_Data\StreamingAssets') + + music_db: dict[str, dict[str, str | int]] = {} + DEST_PATH = Path('../Data/musicDB.json') + + music_folders = [f for f in (A000_DIR / 'music').iterdir() if f.is_dir()] + for option_dir in OPTION_DIR.iterdir(): + if (option_dir / 'music').exists(): + music_folders.extend([f for f in (option_dir / 'music').iterdir() if f.is_dir()]) + + for folder in music_folders: + xml_path = (folder / 'Music.xml') + if xml_path.exists(): + xml = minidom.parse(xml_path.as_posix()) + data = xml.getElementsByTagName('MusicData')[0] + music_id = data.getElementsByTagName('name')[0].getElementsByTagName('id')[0].firstChild.data + music_name = data.getElementsByTagName('name')[0].getElementsByTagName('str')[0].firstChild.data + music_version = data.getElementsByTagName('AddVersion')[0].getElementsByTagName('id')[0].firstChild.data + music_db[music_id] = { + "name": music_name, + "version": int(music_version) + } + logger.debug(f'Found {len(music_db)} music data') + serialized = '{\n' + sorted_keys = sorted(music_db.keys(), key=lambda x: int(x)) + for key in sorted_keys: + value = music_db[key] + serialized += f' "{key}": {json.dumps(value, ensure_ascii=False)},\n' + serialized = serialized[:-2] + '\n}' + + with open(DEST_PATH, 'w', encoding='utf-8') as f: + f.write(serialized) + + +if __name__ == '__main__': + makeMusicDBJson() + print('Done.') \ No newline at end of file diff --git a/backend/Standalone/UI.py b/backend/Standalone/UI.py new file mode 100644 index 0000000..79fdd59 --- /dev/null +++ b/backend/Standalone/UI.py @@ -0,0 +1,113 @@ +import sys +import rapidjson as json + +from PyQt6.QtWidgets import ( + QApplication, + QMainWindow, + QWidget, + QVBoxLayout, + QLineEdit, + QTextEdit, + QPushButton, + QLabel, + QHBoxLayout, +) +from PyQt6.QtCore import Qt + +# 将当前目录的父目录加入到 sys.path 中 +from pathlib import Path + +current_dir = Path(__file__).resolve().parent +parent_dir = current_dir.parent +sys.path.append(str(parent_dir)) + +from API_TitleServer import * + + +def sendRequest(requestText: str, apiNameText: str, uid: int) -> str: + try: + data = json.loads(requestText) + data = json.dumps(data) + except Exception: + return "给出的输入不是有效的 JSON" + try: + result = apiSDGB(data, apiNameText, uid) + except Exception as e: + return "请求失败:" + str(e) + return result + + +class ApiTester(QMainWindow): + def __init__(self): + super().__init__() + + # 主窗口设定 + self.setWindowTitle("舞萌DX 2024 API 测试器") + self.resize(640, 400) + # 布局 + mainWidget = QWidget() + self.setCentralWidget(mainWidget) + MainLayout = QVBoxLayout(mainWidget) + + # 目标 API 输入框布局 + TargetAPILayout = QHBoxLayout() + # API 输入框 + self.TargetAPIInputBox = QLineEdit() + self.TargetAPIInputBox.setPlaceholderText("指定 API") + TargetAPILayout.addWidget(self.TargetAPIInputBox) + # API 后缀标签 + TargetAPILabel = QLabel("MaimaiChn") + TargetAPILayout.addWidget(TargetAPILabel) + # 添加到主布局 + MainLayout.addLayout(TargetAPILayout) + + # UA额外信息输入框 + self.AgentExtraInputBox = QLineEdit() + self.AgentExtraInputBox.setPlaceholderText("指定附加信息(UID或狗号)") + MainLayout.addWidget(self.AgentExtraInputBox) + + # 请求输入框 + self.RequestInputBox = QTextEdit() + self.RequestInputBox.setPlaceholderText("此处填入请求") + MainLayout.addWidget(self.RequestInputBox) + # 发送按钮 + SendRequestButton = QPushButton("发送!") + SendRequestButton.clicked.connect(self.prepareRequest) + MainLayout.addWidget(SendRequestButton) + # 响应输出框 + self.ResponseTextBox = QTextEdit() + self.ResponseTextBox.setPlaceholderText("此处显示输出") + self.ResponseTextBox.setReadOnly(True) + MainLayout.addWidget(self.ResponseTextBox) + + # 布局设定 + MainLayout.setContentsMargins(5, 5, 5, 5) + MainLayout.setSpacing(5) + MainLayout.setAlignment(Qt.AlignmentFlag.AlignTop) + + def prepareRequest(self): + # 发送请求用 + try: + RequestDataString = self.RequestInputBox.toPlainText() + TargetAPIString = self.TargetAPIInputBox.text() + AgentExtraString = int(self.AgentExtraInputBox.text()) + except Exception: + self.ResponseTextBox.setPlainText("输入无效") + return + + Result = sendRequest(RequestDataString, TargetAPIString, AgentExtraString) + + # 显示出输出 + self.ResponseTextBox.setPlainText(Result) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + # Set proper style for each OS + # if sys.platform == "win32": + # app.setStyle("windowsvista") + # else: + # app.setStyle("Fusion") + window = ApiTester() + window.show() + sys.exit(app.exec()) diff --git a/backend/_Special.py b/backend/_Special.py new file mode 100644 index 0000000..cb2f204 --- /dev/null +++ b/backend/_Special.py @@ -0,0 +1,55 @@ +# 纯纯测试用 + +from loguru import logger + +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from HelperFullPlay import implFullPlayAction, generateMusicData + +from MyConfig import testUid8 + + +def implChangeVersionNumber( + userId: int, + currentLoginTimestamp: int, + currentLoginResult, + dataVersion="1.40.09", + romVersion="1.41.00", +) -> str: + musicData = generateMusicData() + userAllPatches = { + "upsertUserAll": { + "userData": [ + { + "lastRomVersion": romVersion, + "lastDataVersion": dataVersion, + "playerRating": 114514, + } + ], + "userMusicDetailList": [musicData], + "isNewMusicDetailList": "1", # 1避免覆盖 + } + } + result = implFullPlayAction( + userId, currentLoginTimestamp, currentLoginResult, musicData, userAllPatches + ) + return result + + +if __name__ == "__main__": + userId = testUid8 + currentLoginTimestamp = generateTimestamp() + loginResult = apiLogin(currentLoginTimestamp, userId) + + if loginResult["returnCode"] != 1: + logger.info("登录失败") + exit() + try: + logger.info( + implChangeVersionNumber( + userId, currentLoginTimestamp, loginResult, "1.00.00", "1.00.00" + ) + ) + logger.info(apiLogout(currentLoginTimestamp, userId)) + finally: + logger.info(apiLogout(currentLoginTimestamp, userId)) + # logger.warning("Error") diff --git a/backend/main.py b/backend/main.py new file mode 100644 index 0000000..68361d8 --- /dev/null +++ b/backend/main.py @@ -0,0 +1,554 @@ +import uvicorn +import json +import os +from datetime import datetime +from fastapi import FastAPI, Body +from fastapi.middleware.cors import CORSMiddleware +from loguru import logger +from typing import Annotated + +# 创建logs目录(如果不存在) +if not os.path.exists("logs"): + os.makedirs("logs") + +# 配置日志 +logger.add("logs/app_{time}.log", rotation="100 MB", retention="30 days") +logger.add("logs/error_{time}.log", level="ERROR", rotation="100 MB", retention="90 days") + +# API Imports +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from GetUserData import apiGetUserData +from HelperUserAll import generateFullUserAll +from HelperGetUserMusicDetail import getUserFullMusicDetail +from Best50_To_Diving_Fish import implUserMusicToDivingFish +from GetPreview import apiGetUserPreview +from ActionLoginBonus import implLoginBonus +from ActionUnlockItem import implUnlockMultiItem +from ActionScoreRecord import implUploadMusicRecord, implDeleteMusicRecord +from ChargeTicket import implBuyTicket, implWipeTickets +from GetAny import apiGetAny +from API_AuthLiteDelivery import getRawDelivery, parseRawDelivery, getUpdateIniFromURL, parseUpdateIni +from API_AimeDB import implGetUID +from APILogger import api_logger +import uvicorn +import json +import os +from datetime import datetime +from fastapi import FastAPI, Body +from fastapi.middleware.cors import CORSMiddleware +from loguru import logger +from typing import Annotated + +# 创建logs目录(如果不存在) +if not os.path.exists("logs"): + os.makedirs("logs") + +# 配置日志 +logger.add("logs/app_{time}.log", rotation="100 MB", retention="30 days") +logger.add("logs/error_{time}.log", level="ERROR", rotation="100 MB", retention="90 days") + +# API Imports +from HelperLogInOut import apiLogin, apiLogout, generateTimestamp +from GetUserData import apiGetUserData +from HelperUserAll import generateFullUserAll +from HelperGetUserMusicDetail import getUserFullMusicDetail +from Best50_To_Diving_Fish import implUserMusicToDivingFish +from GetPreview import apiGetUserPreview +from ActionLoginBonus import implLoginBonus +from ActionUnlockItem import implUnlockMultiItem +from ActionScoreRecord import implUploadMusicRecord, implDeleteMusicRecord +from ChargeTicket import implBuyTicket, implWipeTickets +from GetAny import apiGetAny +from API_AuthLiteDelivery import getRawDelivery, parseRawDelivery, getUpdateIniFromURL, parseUpdateIni +from API_AimeDB import implGetUID +from APILogger import api_logger +from MyConfig import testUid8 + +app = FastAPI() + +# 日志中间件 +@app.middleware("http") +async def log_requests(request, call_next): + # 记录请求开始时间 + start_time = datetime.now() + + # 记录请求信息 + logger.info(f"Request: {request.method} {request.url} - Headers: {dict(request.headers)}") + + # 处理请求 + response = await call_next(request) + + # 记录响应信息和处理时间 + process_time = (datetime.now() - start_time).total_seconds() + logger.info(f"Response: {response.status_code} - Process Time: {process_time}s") + + return response + +# Set up CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +@app.get("/api") +async def root(): + return {"message": "Welcome to the maimaiDX API"} + +@app.get("/api/music") +async def get_music(): + try: + with open("Data/musicDB.json", "r", encoding="utf-8") as f: + music_data_obj = json.load(f) + + # Convert the object to a list of objects + music_list = [ + { + "id": int(music_id), + "name": details.get("name"), + "version": details.get("version") + } + for music_id, details in music_data_obj.items() + ] + return music_list + + except FileNotFoundError: + return {"error": "Music database not found."} + except Exception as e: + return {"error": str(e)} + +@app.get("/api/user/all") +async def get_user_all(userId: int = None): + if userId is None: + userId = testUid8 + + timestamp = None + try: + # 1. Login + timestamp = generateTimestamp() + login_result = apiLogin(timestamp, userId) + if login_result.get("returnCode") != 1: + return {"error": "Login failed", "details": login_result} + + # 2. Get basic user data + user_data_str = apiGetUserData(userId, noLog=True) + user_data = json.loads(user_data_str) + + if "userData" not in user_data: + return {"error": "GetUserData failed", "details": user_data} + current_user_data2 = user_data["userData"] + + # 3. Generate the full UserAll object + full_user_all = generateFullUserAll( + userId, + login_result, + timestamp, + current_user_data2, + {} + ) + + # 4. Logout + apiLogout(timestamp, userId) + + return full_user_all + + except Exception as e: + logger.error(f"An error occurred in get_user_all: {e}") + if timestamp and userId: + try: + apiLogout(timestamp, userId) + except Exception as logout_e: + logger.error(f"Error during cleanup logout: {logout_e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.get("/api/user/music_results") +async def get_user_music_results(userId: int): + if not userId: + return {"error": "userId is required"} + try: + # This might be slow as it fetches all pages + full_music_details = getUserFullMusicDetail(userId) + return {"userMusicResults": full_music_details} + except Exception as e: + logger.error(f"An error occurred in get_user_music_results: {e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/user/upload_to_diving_fish") +async def upload_to_diving_fish( + data: dict +): + userId = data.get("userId") + fishImportToken = data.get("fishImportToken") + if not userId or not fishImportToken: + return {"error": "userId and fishImportToken are required"} + try: + result_code = implUserMusicToDivingFish(userId, fishImportToken) + error_map = { + 0: "Success", + 1: "Failed to get user music from game server.", + 2: "Diving Fish authentication failed. Check your token.", + 3: "Communication error with Diving Fish server." + } + return { + "resultCode": result_code, + "message": error_map.get(result_code, "Unknown error") + } + except Exception as e: + logger.error(f"An error occurred in upload_to_diving_fish: {e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.get("/api/user/preview") +async def get_user_preview(userId: int): + if not userId: + return {"error": "userId is required"} + try: + preview_data_str = apiGetUserPreview(userId, noLog=True) + return json.loads(preview_data_str) + except Exception as e: + logger.error(f"An error occurred in get_user_preview: {e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/action/claim_login_bonus") +async def claim_login_bonus(data: dict): + userId = data.get("userId") + if not userId: + return {"error": "userId is required"} + + timestamp = None + try: + # 1. Login + timestamp = generateTimestamp() + login_result = apiLogin(timestamp, userId) + if login_result.get("returnCode") != 1: + return {"error": "Login failed", "details": login_result} + + # 2. Claim bonus + # Using mode 2 to claim all available bonuses + bonus_result = implLoginBonus(userId, timestamp, login_result, bonusGenerateMode=2) + + # 3. Logout + apiLogout(timestamp, userId) + + if bonus_result: + return {"message": "Login bonus action completed successfully.", "details": bonus_result} + else: + return {"message": "No applicable login bonus found or action failed."} + + except Exception as e: + logger.error(f"An error occurred in claim_login_bonus: {e}") + if timestamp and userId: + try: + apiLogout(timestamp, userId) + except Exception as logout_e: + logger.error(f"Error during cleanup logout: {logout_e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/action/unlock_item") +async def unlock_item(data: dict): + userId = data.get("userId") + itemKind = data.get("itemKind") + itemIds_str = data.get("itemIds") # Expecting a comma-separated string of integers + + if not all([userId, itemKind is not None, itemIds_str]): + return {"error": "userId, itemKind, and a string of itemIds are required"} + + try: + itemIds = [int(i.strip()) for i in itemIds_str.split(",")] + except ValueError: + return {"error": "itemIds must be a comma-separated string of numbers"} + + timestamp = None + try: + # 1. Login + timestamp = generateTimestamp() + login_result = apiLogin(timestamp, userId) + if login_result.get("returnCode") != 1: + return {"error": "Login failed", "details": login_result} + + # 2. Unlock items + unlock_result = implUnlockMultiItem( + itemKind, userId, timestamp, login_result, *itemIds + ) + + # 3. Logout + apiLogout(timestamp, userId) + + return {"message": "Unlock action completed.", "details": unlock_result} + + except Exception as e: + logger.error(f"An error occurred in unlock_item: {e}") + if timestamp and userId: + try: + apiLogout(timestamp, userId) + except Exception as logout_e: + logger.error(f"Error during cleanup logout: {logout_e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/action/upload_score") +async def upload_score(data: dict): + userId = data.get("userId") + musicId = data.get("musicId") + levelId = data.get("levelId") + achievement = data.get("achievement") + dxScore = data.get("dxScore") + + if not all([userId, musicId, levelId is not None, achievement is not None, dxScore is not None]): + return {"error": "userId, musicId, levelId, achievement, and dxScore are required"} + + timestamp = None + try: + # 1. Login + timestamp = generateTimestamp() + login_result = apiLogin(timestamp, userId) + if login_result.get("returnCode") != 1: + return {"error": "Login failed", "details": login_result} + + # 2. Upload score + upload_result = implUploadMusicRecord( + userId, timestamp, login_result, musicId, levelId, achievement, dxScore + ) + + # 3. Logout + apiLogout(timestamp, userId) + + return {"message": "Score upload action completed.", "details": upload_result} + + except Exception as e: + logger.error(f"An error occurred in upload_score: {e}") + if timestamp and userId: + try: + apiLogout(timestamp, userId) + except Exception as logout_e: + logger.error(f"Error during cleanup logout: {logout_e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/action/delete_score") +async def delete_score(data: dict): + userId = data.get("userId") + musicId = data.get("musicId") + levelId = data.get("levelId") + + if not all([userId, musicId, levelId is not None]): + return {"error": "userId, musicId, and levelId are required"} + + timestamp = None + try: + # 1. Login + timestamp = generateTimestamp() + login_result = apiLogin(timestamp, userId) + if login_result.get("returnCode") != 1: + return {"error": "Login failed", "details": login_result} + + # 2. Delete score + delete_result = implDeleteMusicRecord( + userId, timestamp, login_result, musicId, levelId + ) + + # 3. Logout + apiLogout(timestamp, userId) + + return {"message": "Score delete action completed.", "details": delete_result} + + except Exception as e: + logger.error(f"An error occurred in delete_score: {e}") + if timestamp and userId: + try: + apiLogout(timestamp, userId) + except Exception as logout_e: + logger.error(f"Error during cleanup logout: {logout_e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/action/buy_ticket") +async def buy_ticket(data: dict): + userId = data.get("userId") + ticketType = data.get("ticketType") + if not all([userId, ticketType is not None]): + return {"error": "userId and ticketType are required"} + + try: + ticketType = int(ticketType) + except ValueError: + return {"error": "ticketType must be a number"} + + timestamp = None + try: + # 1. Login + timestamp = generateTimestamp() + login_result = apiLogin(timestamp, userId) + if login_result.get("returnCode") != 1: + return {"error": "Login failed", "details": login_result} + + # 2. Buy ticket + buy_result = implBuyTicket(userId, ticketType) + + # 3. Logout + apiLogout(timestamp, userId) + + return {"message": "Buy ticket action completed.", "details": json.loads(buy_result)} + + except Exception as e: + logger.error(f"An error occurred in buy_ticket: {e}") + if timestamp and userId: + try: + apiLogout(timestamp, userId) + except Exception as logout_e: + logger.error(f"Error during cleanup logout: {logout_e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/action/force_logout") +async def force_logout(data: dict): + userId = data.get("userId") + if not userId: + return {"error": "userId is required"} + + try: + # Attempt to logout with the current timestamp + timestamp = generateTimestamp() + logout_result = apiLogout(timestamp, userId, noLog=True) + return {"message": "Force logout attempt sent.", "details": logout_result} + except Exception as e: + logger.error(f"An error occurred in force_logout: {e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/action/wipe_tickets") +async def wipe_tickets(data: dict): + userId = data.get("userId") + if not userId: + return {"error": "userId is required"} + + timestamp = None + try: + # 1. Login + timestamp = generateTimestamp() + login_result = apiLogin(timestamp, userId) + if login_result.get("returnCode") != 1: + return {"error": "Login failed", "details": login_result} + + # 2. Wipe tickets + wipe_result = implWipeTickets(userId, timestamp, login_result) + + # 3. Logout + apiLogout(timestamp, userId) + + return {"message": "Wipe tickets action completed.", "details": wipe_result} + + except Exception as e: + logger.error(f"An error occurred in wipe_tickets: {e}") + if timestamp and userId: + try: + apiLogout(timestamp, userId) + except Exception as logout_e: + logger.error(f"Error during cleanup logout: {logout_e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + + +if __name__ == "__main__": + uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) + +@app.get("/api/get_auth_lite_delivery") +async def get_auth_lite_delivery(): + try: + raw_delivery_str = getRawDelivery() + update_links = parseRawDelivery(raw_delivery_str) + return {"updateLinks": update_links} + except Exception as e: + logger.error(f"An error occurred in get_auth_lite_delivery: {e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.get("/api/get_any") +async def get_any(userId: int, apiName: str): + if not all([userId, apiName]): + return {"error": "userId and apiName are required"} + try: + result_str = apiGetAny(userId, apiName, noLog=True) + return json.loads(result_str) + except Exception as e: + logger.error(f"An error occurred in get_any for {apiName}: {e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/parse_update_ini") +async def parse_update_ini(data: dict): + url = data.get("url") + if not url: + return {"error": "URL is required"} + try: + ini_text = getUpdateIniFromURL(url) + parsed_data = parseUpdateIni(ini_text) + return {"parsedData": parsed_data} + except Exception as e: + logger.error(f"An error occurred parsing INI from {url}: {e}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.post("/api/aime_scan") +async def aime_scan(data: dict): + qr_content = data.get("qrContent") + if not qr_content: + api_logger.log_warning("Aime scan failed: qrContent is required") + return {"error": "qrContent is required"} + try: + api_logger.log_info(f"Aime scan request received for QR content: {qr_content[:20]}...") + result = implGetUID(qr_content) + api_logger.log_info(f"Aime scan completed successfully for QR content: {qr_content[:20]}...") + if "errorID" in result: + api_logger.log_warning(f"Aime scan returned error: {result['errorID']}") + elif "userID" in result: + api_logger.log_info(f"Aime scan returned user ID: {result['userID']}") + return result + except Exception as e: + api_logger.log_error(f"An error occurred in aime_scan: {str(e)}") + return {"error": "An unexpected error occurred.", "details": str(e)} + +@app.get("/api/logs") +async def get_logs(): + """获取日志文件内容""" + try: + import os + import glob + from datetime import datetime + + # 获取最新的日志文件 + log_files = glob.glob("logs/app_*.log") + if not log_files: + return {"logs": []} + + # 按修改时间排序,获取最新的日志文件 + latest_log_file = max(log_files, key=os.path.getmtime) + + # 读取日志文件内容 + with open(latest_log_file, "r", encoding="utf-8") as f: + lines = f.readlines() + + # 解析日志行 + logs = [] + for line in lines[-100:]: # 只返回最后100行 + # 尝试解析loguru格式的日志 + try: + # 格式: 2023-05-15 10:30:45.123 | INFO | message + parts = line.strip().split(" | ") + if len(parts) >= 3: + timestamp_str = parts[0] + level = parts[1].strip() + message = " | ".join(parts[2:]) + + # 解析时间戳 + timestamp = datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S.%f") + + logs.append({ + "timestamp": timestamp.isoformat(), + "level": level, + "message": message + }) + except Exception: + # 如果解析失败,将整行作为消息 + logs.append({ + "timestamp": datetime.now().isoformat(), + "level": "INFO", + "message": line.strip() + }) + + return {"logs": logs} + except Exception as e: + api_logger.log_error(f"Failed to read logs: {str(e)}") + return {"error": "Failed to read logs", "details": str(e)} diff --git a/backend/pyproject.toml b/backend/pyproject.toml new file mode 100644 index 0000000..c4cb7aa --- /dev/null +++ b/backend/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "maimaidx-api" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "datetime>=5.5", + "httpx>=0.28.1", + "loguru>=0.7.3", + "pycryptodome>=3.23.0", + "python-rapidjson>=1.21", + "pytz>=2025.2", + "requests>=2.32.4", +] diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..c77bc94 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,7 @@ +datetime>=5.5 +httpx>=0.28.1 +loguru>=0.7.3 +pycryptodome>=3.23.0 +python-rapidjson>=1.21 +pytz>=2025.2 +requests>=2.32.4 \ No newline at end of file diff --git a/backend/uv.lock b/backend/uv.lock new file mode 100644 index 0000000..13c262b --- /dev/null +++ b/backend/uv.lock @@ -0,0 +1,317 @@ +version = 1 +revision = 2 +requires-python = ">=3.12" + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, +] + +[[package]] +name = "certifi" +version = "2025.7.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "datetime" +version = "5.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytz" }, + { name = "zope-interface" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/66/e284b9978fede35185e5d18fb3ae855b8f573d8c90a56de5f6d03e8ef99e/DateTime-5.5.tar.gz", hash = "sha256:21ec6331f87a7fcb57bd7c59e8a68bfffe6fcbf5acdbbc7b356d6a9a020191d3", size = 63671, upload-time = "2024-03-21T07:26:50.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/78/8e382b8cb4346119e2e04270b6eb4a01c5ee70b47a8a0244ecdb157204f7/DateTime-5.5-py3-none-any.whl", hash = "sha256:0abf6c51cb4ba7cee775ca46ccc727f3afdde463be28dbbe8803631fefd4a120", size = 52649, upload-time = "2024-03-21T07:26:47.849Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, +] + +[[package]] +name = "maimaidx-api" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "datetime" }, + { name = "httpx" }, + { name = "loguru" }, + { name = "pycryptodome" }, + { name = "python-rapidjson" }, + { name = "pytz" }, + { name = "requests" }, +] + +[package.metadata] +requires-dist = [ + { name = "datetime", specifier = ">=5.5" }, + { name = "httpx", specifier = ">=0.28.1" }, + { name = "loguru", specifier = ">=0.7.3" }, + { name = "pycryptodome", specifier = ">=3.23.0" }, + { name = "python-rapidjson", specifier = ">=1.21" }, + { name = "pytz", specifier = ">=2025.2" }, + { name = "requests", specifier = ">=2.32.4" }, +] + +[[package]] +name = "pycryptodome" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" }, + { url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" }, + { url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" }, + { url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" }, + { url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" }, + { url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" }, + { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, + { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, + { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, + { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, + { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, + { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, + { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, +] + +[[package]] +name = "python-rapidjson" +version = "1.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/da/f041c85e7852ddb87dd59e34fdbfebf27defc6e4dfecbc0486ab30553dfd/python_rapidjson-1.21.tar.gz", hash = "sha256:4d0dd9cf1fcb6f4bf79ee606e6e9be4cfa598f273b91338a6974b6a99309a1e6", size = 238903, upload-time = "2025-07-10T06:30:43.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/c8/52735f227a9c662df8e4292af2b13960f0351795b0115f9fe95b3bfcfe62/python_rapidjson-1.21-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:354ac34828a242eec1896901fabe59d14adab7a5054d09a8bdaed4c6ee510b22", size = 220850, upload-time = "2025-07-10T07:19:00.767Z" }, + { url = "https://files.pythonhosted.org/packages/c2/83/f02aa1c9ea2594f05dd8c6c51e2ecea398a462118c7f4ddc004a135096f0/python_rapidjson-1.21-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ddd846b095fad357e4e4c5071e874f9c100c2e5b9765abcc7d71254820251c20", size = 210311, upload-time = "2025-07-10T07:19:02.27Z" }, + { url = "https://files.pythonhosted.org/packages/2f/60/b6a4026fd144fe80db1d6f7cb78191ed2f27fb46c69c719b3b63a8542072/python_rapidjson-1.21-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7faf4211354c1db9b21934d723f3c7db1d611ec3013458dc2fe1a732aa100d3e", size = 1679351, upload-time = "2025-07-10T07:19:04.419Z" }, + { url = "https://files.pythonhosted.org/packages/35/7e/73ca21eb527fbe27b267ea04b9047fdfb4a524b2ce2565a082d84cdee7e9/python_rapidjson-1.21-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:46ea95e9292260fd8174966eca05fad08b43ca0f5df1ccfab9796edb0868d8eb", size = 1722281, upload-time = "2025-07-10T07:19:06.637Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9c/23da2d95b0b4de46695b57a593763ddb8e7f8a786c2793be89fc3dad957e/python_rapidjson-1.21-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1cac86399cfbaf24e72aef6eed1114180a72ba0242c7153f89d874a81fb83c6", size = 1719283, upload-time = "2025-07-10T07:19:07.958Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c1/52de9e4c137ccc98116c16835d9a708436d58ba51ba448e238ce08938d5b/python_rapidjson-1.21-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a4afbf54eb60dc56d4c2b6fac5d24709e8a5928baaeff0669c00950961c7a65", size = 2530062, upload-time = "2025-07-10T07:19:09.302Z" }, + { url = "https://files.pythonhosted.org/packages/7f/16/08d24136b5895157f1fdc75669796283d5116336c0b1c27fa73de260980f/python_rapidjson-1.21-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1e8d4d1285c697325ad1b5300866176d190625bbdd85dce963300fad5bfaf166", size = 2635763, upload-time = "2025-07-10T07:19:10.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a3/08aecff00e26e5dbb8cc8e5b2372b6f9ada28fcf09623e6de62f08ed2b99/python_rapidjson-1.21-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1525778bc9b9cee9b8f3058e0435f0e4ff03e2d8c3883893ebcaf58680316964", size = 2635419, upload-time = "2025-07-10T07:19:12.791Z" }, + { url = "https://files.pythonhosted.org/packages/38/21/bd4e0cda3eada4d99562c1b5bcd2cb54588df17d63ab95b8b76af3d93826/python_rapidjson-1.21-cp312-cp312-win32.whl", hash = "sha256:b5717ddb9788ca00e0f97546cbdd3cfd0c25e52ab3bfed0951c7898e7f37cc60", size = 128528, upload-time = "2025-07-10T07:19:14.61Z" }, + { url = "https://files.pythonhosted.org/packages/e2/91/70f9083930de93e7ce9ccebd8bd598a5737829452c03c258ea862146bbfa/python_rapidjson-1.21-cp312-cp312-win_amd64.whl", hash = "sha256:409704e52ad25df661265dbcb3a512a2146c98f72f362adc157e9b595704e1be", size = 148421, upload-time = "2025-07-10T07:19:16.127Z" }, + { url = "https://files.pythonhosted.org/packages/6d/82/9f1ffd805d6c4b2329046f88b59eec077ea5b2a1894ec23f8fcf22f53e0f/python_rapidjson-1.21-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:863208f50b848b8e10aefcf0da2a67ce38a1044a2ebf83d6355f16f0c7865d4f", size = 220853, upload-time = "2025-07-10T07:19:17.723Z" }, + { url = "https://files.pythonhosted.org/packages/21/d8/9de439a33cef3d1a7672028abcc1a3c8d0c9982f03a73b9cc71055325ee3/python_rapidjson-1.21-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:608ade2fff2788821f25e70af4b1bfbf9ab2b2dc8ad47c91616935c5d31e3b72", size = 210314, upload-time = "2025-07-10T07:19:19.292Z" }, + { url = "https://files.pythonhosted.org/packages/da/93/dbee2d13a1619747652b8c8a921cb8a2e7309c0a142867b4954341c76632/python_rapidjson-1.21-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bf6377c4e9636e20b403776585903d1ff52613a419c5f8453032146f0422634", size = 1679359, upload-time = "2025-07-10T07:19:21.086Z" }, + { url = "https://files.pythonhosted.org/packages/a1/56/06535489e561f6c2a035a5fa8d3d2ecc97c647fb4e1625a8cb7a1db46e6b/python_rapidjson-1.21-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:74cba817cd9b16a3a54e7ddb3b59980c338c9a28900124502215dcba5db5a276", size = 1722183, upload-time = "2025-07-10T07:19:23.025Z" }, + { url = "https://files.pythonhosted.org/packages/67/0b/7c27a2473edfb9d40eb65e5a225dc2e833a5e355f228502aa19c2bdf6679/python_rapidjson-1.21-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3194bde069c3f03f3c425de7037cca37d337b6c7ac30f42cd2f17e15f1c4da3a", size = 1718857, upload-time = "2025-07-10T07:19:24.369Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/0db35ac49212505c347cdc353ddebd2b4085bfc02090baae552e7b5908b4/python_rapidjson-1.21-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b663932dc7548567028fb735bf4092b50dc373822dc9756a9ade6513541013de", size = 2530213, upload-time = "2025-07-10T07:19:26.062Z" }, + { url = "https://files.pythonhosted.org/packages/02/6c/4972fb83f0419654d3083e0b4881f607cbf0829d64da43b7d2a3a21480a0/python_rapidjson-1.21-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fcb094ecca3d892f1dbd318251579ef66e2b2f06a2a24414ad71c31931d7ffff", size = 2636100, upload-time = "2025-07-10T07:19:27.744Z" }, + { url = "https://files.pythonhosted.org/packages/3e/08/da452b2bd67f88b8232e48f2341f3320ce9315f0bdfed1ca769b0b389acf/python_rapidjson-1.21-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f4cdb4f77b08b7ae121b7ca27ed2d57dcce17f8cd9f1d4593c954c982aba18ba", size = 2635274, upload-time = "2025-07-10T07:19:29.174Z" }, + { url = "https://files.pythonhosted.org/packages/18/2b/b81e89c01ec25078a2326051b691443c90797da0d21e1b6c862128de5238/python_rapidjson-1.21-cp313-cp313-win32.whl", hash = "sha256:be63c0ef87bf26059ee77f5de21d84a1be3659cb6baa8484c237ffe17a8beb56", size = 128530, upload-time = "2025-07-10T07:19:30.743Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/e92189d6142860f949b9679c915a6a3752ccc2345a5f80a3995233c45f9f/python_rapidjson-1.21-cp313-cp313-win_amd64.whl", hash = "sha256:38307a2ef4fddbdc18806a796201fe668c30e60de7cd795943a3f7fd39535c8b", size = 148424, upload-time = "2025-07-10T07:19:32.212Z" }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, +] + +[[package]] +name = "zope-interface" +version = "7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/30/93/9210e7606be57a2dfc6277ac97dcc864fd8d39f142ca194fdc186d596fda/zope.interface-7.2.tar.gz", hash = "sha256:8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe", size = 252960, upload-time = "2024-11-28T08:45:39.224Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/0b/c7516bc3bad144c2496f355e35bd699443b82e9437aa02d9867653203b4a/zope.interface-7.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:086ee2f51eaef1e4a52bd7d3111a0404081dadae87f84c0ad4ce2649d4f708b7", size = 208959, upload-time = "2024-11-28T08:47:47.788Z" }, + { url = "https://files.pythonhosted.org/packages/a2/e9/1463036df1f78ff8c45a02642a7bf6931ae4a38a4acd6a8e07c128e387a7/zope.interface-7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:21328fcc9d5b80768bf051faa35ab98fb979080c18e6f84ab3f27ce703bce465", size = 209357, upload-time = "2024-11-28T08:47:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/07/a8/106ca4c2add440728e382f1b16c7d886563602487bdd90004788d45eb310/zope.interface-7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6dd02ec01f4468da0f234da9d9c8545c5412fef80bc590cc51d8dd084138a89", size = 264235, upload-time = "2024-11-28T09:18:15.56Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ca/57286866285f4b8a4634c12ca1957c24bdac06eae28fd4a3a578e30cf906/zope.interface-7.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e7da17f53e25d1a3bde5da4601e026adc9e8071f9f6f936d0fe3fe84ace6d54", size = 259253, upload-time = "2024-11-28T08:48:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/96/08/2103587ebc989b455cf05e858e7fbdfeedfc3373358320e9c513428290b1/zope.interface-7.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cab15ff4832580aa440dc9790b8a6128abd0b88b7ee4dd56abacbc52f212209d", size = 264702, upload-time = "2024-11-28T08:48:37.363Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c7/3c67562e03b3752ba4ab6b23355f15a58ac2d023a6ef763caaca430f91f2/zope.interface-7.2-cp312-cp312-win_amd64.whl", hash = "sha256:29caad142a2355ce7cfea48725aa8bcf0067e2b5cc63fcf5cd9f97ad12d6afb5", size = 212466, upload-time = "2024-11-28T08:49:14.397Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3b/e309d731712c1a1866d61b5356a069dd44e5b01e394b6cb49848fa2efbff/zope.interface-7.2-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:3e0350b51e88658d5ad126c6a57502b19d5f559f6cb0a628e3dc90442b53dd98", size = 208961, upload-time = "2024-11-28T08:48:29.865Z" }, + { url = "https://files.pythonhosted.org/packages/49/65/78e7cebca6be07c8fc4032bfbb123e500d60efdf7b86727bb8a071992108/zope.interface-7.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15398c000c094b8855d7d74f4fdc9e73aa02d4d0d5c775acdef98cdb1119768d", size = 209356, upload-time = "2024-11-28T08:48:33.297Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/627384b745310d082d29e3695db5f5a9188186676912c14b61a78bbc6afe/zope.interface-7.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:802176a9f99bd8cc276dcd3b8512808716492f6f557c11196d42e26c01a69a4c", size = 264196, upload-time = "2024-11-28T09:18:17.584Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f6/54548df6dc73e30ac6c8a7ff1da73ac9007ba38f866397091d5a82237bd3/zope.interface-7.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398", size = 259237, upload-time = "2024-11-28T08:48:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/b6/66/ac05b741c2129fdf668b85631d2268421c5cd1a9ff99be1674371139d665/zope.interface-7.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b", size = 264696, upload-time = "2024-11-28T08:48:41.161Z" }, + { url = "https://files.pythonhosted.org/packages/0a/2f/1bccc6f4cc882662162a1158cda1a7f616add2ffe322b28c99cb031b4ffc/zope.interface-7.2-cp313-cp313-win_amd64.whl", hash = "sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd", size = 212472, upload-time = "2024-11-28T08:49:56.587Z" }, +] diff --git a/frontend/.editorconfig b/frontend/.editorconfig new file mode 100644 index 0000000..3b510aa --- /dev/null +++ b/frontend/.editorconfig @@ -0,0 +1,8 @@ +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +max_line_length = 100 diff --git a/frontend/.gitattributes b/frontend/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/frontend/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..8ee54e8 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo diff --git a/frontend/.prettierrc.json b/frontend/.prettierrc.json new file mode 100644 index 0000000..29a2402 --- /dev/null +++ b/frontend/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "singleQuote": true, + "printWidth": 100 +} diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..9a4ff7f --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,39 @@ +# temp-vue-project + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). + +## Type Support for `.vue` Imports in TS + +TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. + +## Customize configuration + +See [Vite Configuration Reference](https://vite.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Type-Check, Compile and Minify for Production + +```sh +npm run build +``` + +### Lint with [ESLint](https://eslint.org/) + +```sh +npm run lint +``` diff --git a/frontend/env.d.ts b/frontend/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/frontend/eslint.config.ts b/frontend/eslint.config.ts new file mode 100644 index 0000000..20475f8 --- /dev/null +++ b/frontend/eslint.config.ts @@ -0,0 +1,22 @@ +import { globalIgnores } from 'eslint/config' +import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' +import pluginVue from 'eslint-plugin-vue' +import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' + +// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: +// import { configureVueProject } from '@vue/eslint-config-typescript' +// configureVueProject({ scriptLangs: ['ts', 'tsx'] }) +// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup + +export default defineConfigWithVueTs( + { + name: 'app/files-to-lint', + files: ['**/*.{ts,mts,tsx,vue}'], + }, + + globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), + + pluginVue.configs['flat/essential'], + vueTsConfigs.recommended, + skipFormatting, +) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..ca47573 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + maimaiDX-API web tools + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..c845b43 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,5707 @@ +{ + "name": "temp-vue-project", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "temp-vue-project", + "version": "0.0.0", + "dependencies": { + "@element-plus/icons-vue": "^2.3.2", + "axios": "^1.12.2", + "element-plus": "^2.11.2", + "pinia": "^3.0.3", + "vue": "^3.5.18", + "vue-router": "^4.5.1" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/node": "^22.16.5", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.6.0", + "@vue/tsconfig": "^0.7.0", + "eslint": "^9.31.0", + "eslint-plugin-vue": "~10.3.0", + "jiti": "^2.4.2", + "npm-run-all2": "^8.0.4", + "prettier": "3.6.2", + "typescript": "~5.8.0", + "vite": "^7.0.6", + "vite-plugin-vue-devtools": "^8.0.0", + "vue-tsc": "^3.0.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@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/core/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/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@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-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/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/@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/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/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz", + "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "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/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.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==", + "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/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", + "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", + "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", + "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.2.tgz", + "integrity": "sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", + "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", + "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", + "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", + "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", + "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.2.tgz", + "integrity": "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.2.tgz", + "integrity": "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.50.2.tgz", + "integrity": "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.2.tgz", + "integrity": "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.2.tgz", + "integrity": "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.2.tgz", + "integrity": "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.2.tgz", + "integrity": "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", + "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.2.tgz", + "integrity": "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.2.tgz", + "integrity": "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.2.tgz", + "integrity": "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.2.tgz", + "integrity": "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.2.tgz", + "integrity": "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@tsconfig/node22": { + "version": "22.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.2.tgz", + "integrity": "sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "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/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/node": { + "version": "22.18.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.5.tgz", + "integrity": "sha512-g9BpPfJvxYBXUWI9bV37j6d6LTMNQ88hPwdWWUeYZnMhlo66FIg9gCc1/DZb15QylJSKwOZjwrckvOTWpOiChg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", + "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz", + "integrity": "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.44.0", + "@typescript-eslint/type-utils": "8.44.0", + "@typescript-eslint/utils": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.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.44.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.0.tgz", + "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.44.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0", + "debug": "^4.3.4" + }, + "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", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.0.tgz", + "integrity": "sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.44.0", + "@typescript-eslint/types": "^8.44.0", + "debug": "^4.3.4" + }, + "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.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz", + "integrity": "sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.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.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz", + "integrity": "sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==", + "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.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz", + "integrity": "sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0", + "@typescript-eslint/utils": "8.44.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.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", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz", + "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==", + "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.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz", + "integrity": "sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.44.0", + "@typescript-eslint/tsconfig-utils": "8.44.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/visitor-keys": "8.44.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.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.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.0.tgz", + "integrity": "sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.44.0", + "@typescript-eslint/types": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.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", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz", + "integrity": "sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.44.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.18", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/devtools-core": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.0.2.tgz", + "integrity": "sha512-V7eKTTHoS6KfK8PSGMLZMhGv/9yNDrmv6Qc3r71QILulnzPnqK2frsTyx3e2MrhdUZnENPEm6hcb4z0GZOqNhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.0.2", + "@vue/devtools-shared": "^8.0.2", + "mitt": "^3.0.1", + "nanoid": "^5.1.5", + "pathe": "^2.0.3", + "vite-hot-client": "^2.1.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-core/node_modules/nanoid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", + "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.2.tgz", + "integrity": "sha512-yjZKdEmhJzQqbOh4KFBfTOQjDPMrjjBNCnHBvnTGJX+YLAqoUtY2J+cg7BE+EA8KUv8LprECq04ts75wCoIGWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.0.2", + "birpc": "^2.5.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^2.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.2.tgz", + "integrity": "sha512-mLU0QVdy5Lp40PMGSixDw/Kbd6v5dkQXltd2r+mdVQV7iUog2NlZuLxFZApFZ/mObUBDhoCpf0T3zF2FWWdeHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/eslint-config-prettier": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz", + "integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2" + }, + "peerDependencies": { + "eslint": ">= 8.21.0", + "prettier": ">= 3.0.0" + } + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "14.6.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.6.0.tgz", + "integrity": "sha512-UpiRY/7go4Yps4mYCjkvlIbVWmn9YvPGQDxTAlcKLphyaD77LjIu3plH4Y9zNT0GB4f3K5tMmhhtRhPOgrQ/bQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.35.1", + "fast-glob": "^3.3.3", + "typescript-eslint": "^8.35.1", + "vue-eslint-parser": "^10.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0", + "eslint-plugin-vue": "^9.28.0 || ^10.0.0", + "typescript": ">=4.8.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.0.7.tgz", + "integrity": "sha512-0sqqyqJ0Gn33JH3TdIsZLCZZ8Gr4kwlg8iYOnOrDDkJKSjFurlQY/bEFQx5zs7SX2C/bjMkmPYq/NiyY1fTOkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^2.0.5", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", + "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz", + "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz", + "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/runtime-core": "3.5.21", + "@vue/shared": "3.5.21", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz", + "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "vue": "3.5.21" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.7.0.tgz", + "integrity": "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz", + "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.16", + "@vueuse/metadata": "9.13.0", + "@vueuse/shared": "9.13.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz", + "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz", + "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", + "license": "MIT", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "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.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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/alien-signals": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.7.tgz", + "integrity": "sha512-wE7y3jmYeb0+h6mr5BOovuqhFv22O/MV9j5p0ndJsa7z1zJNPGQ4ph5pQk/kTTCWRC3xsA4SmtwmkzQO+7NCNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz", + "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.4.tgz", + "integrity": "sha512-L+YvJwGAgwJBV1p6ffpSTa2KRc69EeeYGYjRVWKs0GKrK+LON0GC0gV+rKSNtALEDvMDqkvCFq9r1r94/Gjwxw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/birpc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz", + "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "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.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "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/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "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/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "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/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", + "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "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/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.219", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.219.tgz", + "integrity": "sha512-JqaXfxHOS0WvKweEnrPHWRm8cnPVbdB7vXCQHPPFoAJFM3xig5/+/H08ZVkvJf4unvj8yncKy6MerOPj1NW1GQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/element-plus": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.11.2.tgz", + "integrity": "sha512-sTMDXtgeqy17TUsBSH/DL3h1/5sqIOVUUgXFoVbdD8lWWYssKrDX50CEYy4k29tYJhPHKZyFSwcLJsIajr+dDA==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^3.4.1", + "@element-plus/icons-vue": "^2.3.1", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", + "@types/lodash": "^4.14.182", + "@types/lodash-es": "^4.17.6", + "@vueuse/core": "^9.1.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.13", + "escape-html": "^1.0.3", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "lodash-unified": "^1.0.2", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "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-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "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": "9.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", + "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.35.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.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", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.3.0.tgz", + "integrity": "sha512-A0u9snqjCfYaPnqqOaH6MBLVWDUIN4trXn8J3x67uDcXvR7X6Ut8p16N+nYhMCQ9Y7edg2BIRGzfyZsY0IdqoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "vue-eslint-parser": "^10.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/parser": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "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/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "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/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "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-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "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/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "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/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "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/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "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/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/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "license": "MIT", + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "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/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "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.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", + "license": "BSD-3-Clause" + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz", + "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" + } + }, + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm-run-all2/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "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/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pinia": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.3.tgz", + "integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/@vue/devtools-api": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz", + "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.7" + } + }, + "node_modules/pinia/node_modules/@vue/devtools-kit": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", + "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.7", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/pinia/node_modules/@vue/devtools-shared": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", + "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/pinia/node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "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.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "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.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "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/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.2.tgz", + "integrity": "sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.50.2", + "@rollup/rollup-android-arm64": "4.50.2", + "@rollup/rollup-darwin-arm64": "4.50.2", + "@rollup/rollup-darwin-x64": "4.50.2", + "@rollup/rollup-freebsd-arm64": "4.50.2", + "@rollup/rollup-freebsd-x64": "4.50.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", + "@rollup/rollup-linux-arm-musleabihf": "4.50.2", + "@rollup/rollup-linux-arm64-gnu": "4.50.2", + "@rollup/rollup-linux-arm64-musl": "4.50.2", + "@rollup/rollup-linux-loong64-gnu": "4.50.2", + "@rollup/rollup-linux-ppc64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-musl": "4.50.2", + "@rollup/rollup-linux-s390x-gnu": "4.50.2", + "@rollup/rollup-linux-x64-gnu": "4.50.2", + "@rollup/rollup-linux-x64-musl": "4.50.2", + "@rollup/rollup-openharmony-arm64": "4.50.2", + "@rollup/rollup-win32-arm64-msvc": "4.50.2", + "@rollup/rollup-win32-ia32-msvc": "4.50.2", + "@rollup/rollup-win32-x64-msvc": "4.50.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/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/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "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": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.0.tgz", + "integrity": "sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.44.0", + "@typescript-eslint/parser": "8.44.0", + "@typescript-eslint/typescript-estree": "8.44.0", + "@typescript-eslint/utils": "8.44.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", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.0.tgz", + "integrity": "sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-utils/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "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", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.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 + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "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-dev-rpc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz", + "integrity": "sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==", + "dev": true, + "license": "MIT", + "dependencies": { + "birpc": "^2.4.0", + "vite-hot-client": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0" + } + }, + "node_modules/vite-hot-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.1.0.tgz", + "integrity": "sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz", + "integrity": "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.1.0", + "debug": "^4.4.1", + "error-stack-parser-es": "^1.0.5", + "ohash": "^2.0.11", + "open": "^10.2.0", + "perfect-debounce": "^2.0.0", + "sirv": "^3.0.1", + "unplugin-utils": "^0.3.0", + "vite-dev-rpc": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-vue-devtools": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.0.2.tgz", + "integrity": "sha512-1069qvMBcyAu3yXQlvYrkwoyLOk0lSSR/gTKy/vy+Det7TXnouGei6ZcKwr5TIe938v/14oLlp0ow6FSJkkORA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-core": "^8.0.2", + "@vue/devtools-kit": "^8.0.2", + "@vue/devtools-shared": "^8.0.2", + "execa": "^9.6.0", + "sirv": "^3.0.2", + "vite-plugin-inspect": "^11.3.3", + "vite-plugin-vue-inspector": "^5.3.2" + }, + "engines": { + "node": ">=v14.21.3" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + } + }, + "node_modules/vite-plugin-vue-inspector": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.3.2.tgz", + "integrity": "sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/plugin-proposal-decorators": "^7.23.0", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/babel-plugin-jsx": "^1.1.5", + "@vue/compiler-dom": "^3.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.4" + }, + "peerDependencies": { + "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vite/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/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", + "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-sfc": "3.5.21", + "@vue/runtime-dom": "3.5.21", + "@vue/server-renderer": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz", + "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.6.0", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.0.7.tgz", + "integrity": "sha512-BSMmW8GGEgHykrv7mRk6zfTdK+tw4MBZY/x6fFa7IkdXK3s/8hQRacPjG9/8YKFDIWGhBocwi6PlkQQ/93OgIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.23", + "@vue/language-core": "3.0.7" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "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/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "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/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..ecd7d23 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,43 @@ +{ + "name": "temp-vue-project", + "version": "0.0.0", + "private": true, + "type": "module", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "scripts": { + "dev": "vite", + "build": "run-p type-check \"build-only {@}\" --", + "preview": "vite preview", + "build-only": "vite build", + "type-check": "vue-tsc --build", + "lint": "eslint . --fix", + "format": "prettier --write src/" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.2", + "axios": "^1.12.2", + "element-plus": "^2.11.2", + "pinia": "^3.0.3", + "vue": "^3.5.18", + "vue-router": "^4.5.1" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/node": "^22.16.5", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/eslint-config-prettier": "^10.2.0", + "@vue/eslint-config-typescript": "^14.6.0", + "@vue/tsconfig": "^0.7.0", + "eslint": "^9.31.0", + "eslint-plugin-vue": "~10.3.0", + "jiti": "^2.4.2", + "npm-run-all2": "^8.0.4", + "prettier": "3.6.2", + "typescript": "~5.8.0", + "vite": "^7.0.6", + "vite-plugin-vue-devtools": "^8.0.0", + "vue-tsc": "^3.0.4" + } +} diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/frontend/public/favicon.ico differ diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..af56b81 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/frontend/src/assets/logo.svg b/frontend/src/assets/logo.svg new file mode 100644 index 0000000..7565660 --- /dev/null +++ b/frontend/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css new file mode 100644 index 0000000..eb6dd0e --- /dev/null +++ b/frontend/src/assets/main.css @@ -0,0 +1,10 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#app { + padding: 20px; +} diff --git a/frontend/src/components/HelloWorld.vue b/frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000..d174cf8 --- /dev/null +++ b/frontend/src/components/HelloWorld.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/frontend/src/components/LogViewer.vue b/frontend/src/components/LogViewer.vue new file mode 100644 index 0000000..41ca3d5 --- /dev/null +++ b/frontend/src/components/LogViewer.vue @@ -0,0 +1,136 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/TheWelcome.vue b/frontend/src/components/TheWelcome.vue new file mode 100644 index 0000000..6092dff --- /dev/null +++ b/frontend/src/components/TheWelcome.vue @@ -0,0 +1,94 @@ + + + diff --git a/frontend/src/components/WelcomeItem.vue b/frontend/src/components/WelcomeItem.vue new file mode 100644 index 0000000..6d7086a --- /dev/null +++ b/frontend/src/components/WelcomeItem.vue @@ -0,0 +1,87 @@ + + + diff --git a/frontend/src/components/icons/IconCommunity.vue b/frontend/src/components/icons/IconCommunity.vue new file mode 100644 index 0000000..2dc8b05 --- /dev/null +++ b/frontend/src/components/icons/IconCommunity.vue @@ -0,0 +1,7 @@ + diff --git a/frontend/src/components/icons/IconDocumentation.vue b/frontend/src/components/icons/IconDocumentation.vue new file mode 100644 index 0000000..6d4791c --- /dev/null +++ b/frontend/src/components/icons/IconDocumentation.vue @@ -0,0 +1,7 @@ + diff --git a/frontend/src/components/icons/IconEcosystem.vue b/frontend/src/components/icons/IconEcosystem.vue new file mode 100644 index 0000000..c3a4f07 --- /dev/null +++ b/frontend/src/components/icons/IconEcosystem.vue @@ -0,0 +1,7 @@ + diff --git a/frontend/src/components/icons/IconSupport.vue b/frontend/src/components/icons/IconSupport.vue new file mode 100644 index 0000000..7452834 --- /dev/null +++ b/frontend/src/components/icons/IconSupport.vue @@ -0,0 +1,7 @@ + diff --git a/frontend/src/components/icons/IconTooling.vue b/frontend/src/components/icons/IconTooling.vue new file mode 100644 index 0000000..660598d --- /dev/null +++ b/frontend/src/components/icons/IconTooling.vue @@ -0,0 +1,19 @@ + + diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..acb0a98 --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,20 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import ElementPlus from 'element-plus' +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import 'element-plus/dist/index.css' + +import App from './App.vue' +import router from './router' + +const app = createApp(App) + +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} + +app.use(createPinia()) +app.use(router) +app.use(ElementPlus) + +app.mount('#app') \ No newline at end of file diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts new file mode 100644 index 0000000..777f79a --- /dev/null +++ b/frontend/src/router/index.ts @@ -0,0 +1,45 @@ +import { createRouter, createWebHistory } from 'vue-router' +import HomeView from '../views/HomeView.vue' +import MusicView from '../views/MusicView.vue' +import ProfileView from '../views/ProfileView.vue' +import AuthLiteDeliveryView from '../views/AuthLiteDeliveryView.vue' +import AimeDBView from '../views/AimeDBView.vue' +import LogsView from '../views/LogsView.vue' + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/', + name: 'home', + component: HomeView + }, + { + path: '/music', + name: 'music', + component: MusicView + }, + { + path: '/profile', + name: 'profile', + component: ProfileView + }, + { + path: '/aime-db', + name: 'aime-db', + component: AimeDBView + }, + { + path: '/auth-lite-delivery', + name: 'auth-lite-delivery', + component: AuthLiteDeliveryView + }, + { + path: '/logs', + name: 'logs', + component: LogsView + } + ] +}) + +export default router diff --git a/frontend/src/utils/logger.ts b/frontend/src/utils/logger.ts new file mode 100644 index 0000000..8945bf9 --- /dev/null +++ b/frontend/src/utils/logger.ts @@ -0,0 +1,143 @@ +// 日志级别枚举 +const LogLevel = { + DEBUG: 0, + INFO: 1, + WARN: 2, + ERROR: 3 +}; + +// 当前日志级别 +let currentLogLevel = LogLevel.INFO; + +// 日志缓冲区 +let logBuffer = []; +const MAX_BUFFER_SIZE = 1000; + +// 设置日志级别 +export function setLogLevel(level) { + currentLogLevel = level; +} + +// 获取当前时间戳 +function getTimestamp() { + return new Date().toISOString(); +} + +// 写入日志到缓冲区 +function writeToBuffer(level, message, data = null) { + const logEntry = { + timestamp: getTimestamp(), + level: Object.keys(LogLevel).find(key => LogLevel[key] === level), + message: message, + data: data + }; + + logBuffer.push(logEntry); + + // 保持缓冲区大小在限制内 + if (logBuffer.length > MAX_BUFFER_SIZE) { + logBuffer = logBuffer.slice(-MAX_BUFFER_SIZE); + } + + // 在控制台输出日志 + switch (level) { + case LogLevel.DEBUG: + console.debug(`[DEBUG] ${message}`, data); + break; + case LogLevel.INFO: + console.info(`[INFO] ${message}`, data); + break; + case LogLevel.WARN: + console.warn(`[WARN] ${message}`, data); + break; + case LogLevel.ERROR: + console.error(`[ERROR] ${message}`, data); + break; + } +} + +// Debug级别日志 +export function logDebug(message, data = null) { + if (currentLogLevel <= LogLevel.DEBUG) { + writeToBuffer(LogLevel.DEBUG, message, data); + } +} + +// Info级别日志 +export function logInfo(message, data = null) { + if (currentLogLevel <= LogLevel.INFO) { + writeToBuffer(LogLevel.INFO, message, data); + } +} + +// Warn级别日志 +export function logWarn(message, data = null) { + if (currentLogLevel <= LogLevel.WARN) { + writeToBuffer(LogLevel.WARN, message, data); + } +} + +// Error级别日志 +export function logError(message, data = null) { + if (currentLogLevel <= LogLevel.ERROR) { + writeToBuffer(LogLevel.ERROR, message, data); + } +} + +// 获取日志缓冲区 +export function getLogBuffer() { + return [...logBuffer]; // 返回副本 +} + +// 清空日志缓冲区 +export function clearLogBuffer() { + logBuffer = []; +} + +// 将日志保存到文件 +export function saveLogsToFile() { + const logs = getLogBuffer(); + const logText = logs.map(entry => + `${entry.timestamp} [${entry.level}] ${entry.message} ${entry.data ? JSON.stringify(entry.data) : ''}` + ).join('\n'); + + const blob = new Blob([logText], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = `frontend-logs-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.txt`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + + URL.revokeObjectURL(url); +} + +// API请求日志 +export function logApiRequest(method, url, data = null) { + logInfo(`API Request: ${method} ${url}`, data); +} + +// API响应日志 +export function logApiResponse(status, data = null) { + if (status >= 400) { + logError(`API Response: ${status}`, data); + } else { + logInfo(`API Response: ${status}`, data); + } +} + +export default { + LogLevel, + setLogLevel, + logDebug, + logInfo, + logWarn, + logError, + getLogBuffer, + clearLogBuffer, + saveLogsToFile, + logApiRequest, + logApiResponse +}; \ No newline at end of file diff --git a/frontend/src/views/AboutView.vue b/frontend/src/views/AboutView.vue new file mode 100644 index 0000000..756ad2a --- /dev/null +++ b/frontend/src/views/AboutView.vue @@ -0,0 +1,15 @@ + + + diff --git a/frontend/src/views/AimeDBView.vue b/frontend/src/views/AimeDBView.vue new file mode 100644 index 0000000..babfe3c --- /dev/null +++ b/frontend/src/views/AimeDBView.vue @@ -0,0 +1,110 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/AuthLiteDeliveryView.vue b/frontend/src/views/AuthLiteDeliveryView.vue new file mode 100644 index 0000000..e0043ff --- /dev/null +++ b/frontend/src/views/AuthLiteDeliveryView.vue @@ -0,0 +1,105 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue new file mode 100644 index 0000000..993a391 --- /dev/null +++ b/frontend/src/views/HomeView.vue @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/frontend/src/views/LogsView.vue b/frontend/src/views/LogsView.vue new file mode 100644 index 0000000..f708dfc --- /dev/null +++ b/frontend/src/views/LogsView.vue @@ -0,0 +1,182 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/MusicView.vue b/frontend/src/views/MusicView.vue new file mode 100644 index 0000000..00b798c --- /dev/null +++ b/frontend/src/views/MusicView.vue @@ -0,0 +1,62 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/ProfileView.vue b/frontend/src/views/ProfileView.vue new file mode 100644 index 0000000..036e9f4 --- /dev/null +++ b/frontend/src/views/ProfileView.vue @@ -0,0 +1,397 @@ + + + + + \ No newline at end of file diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000..913b8f2 --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,12 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..66b5e57 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..a83dfc9 --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/node22/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*", + "eslint.config.*" + ], + "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..937a5e1 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,21 @@ +import { fileURLToPath, URL } from 'node:url' + +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + }, + }, + server: { + hmr: { + overlay: false, + }, + }, +})