支持大文件
Some checks failed
CI/CD / Code Check (push) Has been cancelled
CI/CD / Build Windows (push) Has been cancelled

This commit is contained in:
2026-03-15 09:02:02 +08:00
parent 4654f36202
commit 5c278e1925
12 changed files with 410 additions and 177 deletions

View File

@@ -3,115 +3,210 @@
## 项目分析 ## 项目分析
DeEarthX-CE 是一个与 Minecraft 相关的工具,包含以下组件: DeEarthX-CE 是一个与 Minecraft 相关的工具,包含以下组件:
- **后端**TypeScript + Express 构建,提供模组检测、过滤、平台集成等功能
- **端**Vue 3 + Ant Design Vue + Tauri 构建的桌面应用 * **端**TypeScript + Express 构建,提供模组检测、过滤、平台集成等功能
- **文档**VitePress 构建的文档网站
* **前端**Vue 3 + Ant Design Vue + Tauri 构建的桌面应用
* **文档**VitePress 构建的文档网站
## 改进任务列表 ## 改进任务列表
### [ ] 任务 1代码质量检查与优化 ### \[ ] 任务 1代码质量检查与优化
- **Priority**P1
- **Depends On**None
- **Description**
- 检查并清理未使用的依赖
- 统一代码风格和命名规范
- 优化错误处理机制
- 提高代码可读性和可维护性
- **Success Criteria**
- 所有依赖都是必要的
- 代码风格统一
- 错误处理完善
- **Test Requirements**
- `programmatic` TR-1.1:运行 `npm run build` 无错误
- `programmatic` TR-1.2:运行代码检查工具无严重警告
- `human-judgement` TR-1.3:代码结构清晰,注释完善
### [ ] 任务 2性能优化 * **Priority**P1
- **Priority**P2
- **Depends On**:任务 1
- **Description**
- 优化文件操作性能
- 优化网络请求和响应
- 减少不必要的计算和重复操作
- 提高模组处理速度
- **Success Criteria**
- 文件操作速度提升
- 网络请求响应时间减少
- 模组处理效率提高
- **Test Requirements**
- `programmatic` TR-2.1:模组处理时间减少 20%
- `programmatic` TR-2.2:内存使用降低 15%
- `human-judgement` TR-2.3:用户操作响应更流畅
### [ ] 任务 3安全性增强 * **Depends On**None
- **Priority**P1
- **Depends On**:任务 1
- **Description**
- 检查并修复安全漏洞
- 加强输入验证
- 优化文件操作安全性
- 检查依赖的安全状态
- **Success Criteria**
- 无安全漏洞
- 输入验证完善
- 依赖无安全问题
- **Test Requirements**
- `programmatic` TR-3.1:运行安全扫描工具无严重漏洞
- `programmatic` TR-3.2:所有输入都经过验证
- `human-judgement` TR-3.3:安全措施到位
### [ ] 任务 4功能增强 * **Description**
- **Priority**P2
- **Depends On**:任务 1, 任务 3
- **Description**
- 完善用户界面交互
- 增加更多模组平台支持
- 优化模板管理功能
- 增强多语言支持
- **Success Criteria**
- 用户界面更友好
- 支持更多模组平台
- 模板管理更便捷
- 多语言支持更完善
- **Test Requirements**
- `programmatic` TR-4.1:所有新增功能正常工作
- `human-judgement` TR-4.2:用户界面美观易用
- `human-judgement` TR-4.3:多语言支持准确
### [ ] 任务 5构建和部署优化 * 检查并清理未使用的依赖
- **Priority**P2
- **Depends On**:任务 1, 任务 2
- **Description**
- 优化构建流程
- 减少构建时间
- 优化打包大小
- 完善部署文档
- **Success Criteria**
- 构建流程更高效
- 构建时间减少
- 打包大小优化
- 部署文档完善
- **Test Requirements**
- `programmatic` TR-5.1:构建时间减少 25%
- `programmatic` TR-5.2:打包大小减少 20%
- `human-judgement` TR-5.3:部署文档清晰完整
### [ ] 任务 6测试覆盖度提升 * 统一代码风格和命名规范
- **Priority**P3
- **Depends On**:任务 1 * 优化错误处理机制
- **Description**
- 增加单元测试 * 提高代码可读性和可维护性
- 增加集成测试
- 提高测试覆盖度 * **Success Criteria**
- 建立测试自动化流程
- **Success Criteria** * 所有依赖都是必要的
- 测试覆盖度达到 80% 以上
- 关键功能有测试用例 * 代码风格统一
- 测试自动化流程建立
- **Test Requirements** * 错误处理完善
- `programmatic` TR-6.1:测试覆盖度达到 80% 以上
- `programmatic` TR-6.2:所有测试用例通过 * **Test Requirements**
- `human-judgement` TR-6.3:测试用例设计合理
* `programmatic` TR-1.1:运行 `npm run build` 无错误
* `programmatic` TR-1.2:运行代码检查工具无严重警告
* `human-judgement` TR-1.3:代码结构清晰,注释完善
### \[ ] 任务 2性能优化
* **Priority**P2
* **Depends On**:任务 1
* **Description**
* 优化文件操作性能
* 优化网络请求和响应
* 减少不必要的计算和重复操作
* 提高模组处理速度
* **Success Criteria**
* 文件操作速度提升
* 网络请求响应时间减少
* 模组处理效率提高
* **Test Requirements**
* `programmatic` TR-2.1:模组处理时间减少 20%
* `programmatic` TR-2.2:内存使用降低 15%
* `human-judgement` TR-2.3:用户操作响应更流畅
### \[ ] 任务 3安全性增强
* **Priority**P1
* **Depends On**:任务 1
* **Description**
* 检查并修复安全漏洞
* 加强输入验证
* 优化文件操作安全性
* 检查依赖的安全状态
* **Success Criteria**
* 无安全漏洞
* 输入验证完善
* 依赖无安全问题
* **Test Requirements**
* `programmatic` TR-3.1:运行安全扫描工具无严重漏洞
* `programmatic` TR-3.2:所有输入都经过验证
* `human-judgement` TR-3.3:安全措施到位
### \[ ] 任务 4功能增强
* **Priority**P2
* **Depends On**:任务 1, 任务 3
* **Description**
* 完善用户界面交互
* 增加更多模组平台支持
* 优化模板管理功能
* 增强多语言支持
* **Success Criteria**
* 用户界面更友好
* 支持更多模组平台
* 模板管理更便捷
* 多语言支持更完善
* **Test Requirements**
* `programmatic` TR-4.1:所有新增功能正常工作
* `human-judgement` TR-4.2:用户界面美观易用
* `human-judgement` TR-4.3:多语言支持准确
### \[ ] 任务 5构建和部署优化
* **Priority**P2
* **Depends On**:任务 1, 任务 2
* **Description**
* 优化构建流程
* 减少构建时间
* 优化打包大小
* 完善部署文档
* **Success Criteria**
* 构建流程更高效
* 构建时间减少
* 打包大小优化
* 部署文档完善
* **Test Requirements**
* `programmatic` TR-5.1:构建时间减少 25%
* `programmatic` TR-5.2:打包大小减少 20%
* `human-judgement` TR-5.3:部署文档清晰完整
### \[ ] 任务 6测试覆盖度提升
* **Priority**P3
* **Depends On**:任务 1
* **Description**
* 增加单元测试
* 增加集成测试
* 提高测试覆盖度
* 建立测试自动化流程
* **Success Criteria**
* 测试覆盖度达到 80% 以上
* 关键功能有测试用例
* 测试自动化流程建立
* **Test Requirements**
* `programmatic` TR-6.1:测试覆盖度达到 80% 以上
* `programmatic` TR-6.2:所有测试用例通过
* `human-judgement` TR-6.3:测试用例设计合理
## 实施步骤 ## 实施步骤
@@ -125,9 +220,16 @@ DeEarthX-CE 是一个与 Minecraft 相关的工具,包含以下组件:
## 预期成果 ## 预期成果
通过以上改进DeEarthX-CE 项目将: 通过以上改进DeEarthX-CE 项目将:
- 代码质量更高,更易维护
- 性能更优,响应更快 * 代码质量更高,更易维护
- 安全性更强,更可靠
- 能更完善,用户体验更好 * 能更优,响应更快
- 构建和部署更高效
- 测试覆盖更全面,质量更有保障 * 安全性更强,更可靠
* 功能更完善,用户体验更好
* 构建和部署更高效
* 测试覆盖更全面,质量更有保障

View File

@@ -550,12 +550,12 @@ export class ModCheckService {
jarData = fs.readFileSync(file.filename); jarData = fs.readFileSync(file.filename);
} }
const { Azip } = await import("../utils/ziplib.js"); const { yauzl_promise } = await import("../utils/ziplib.js");
const zipEntries = Azip(jarData); const zipEntries = await yauzl_promise(jarData);
for (const entry of zipEntries) { for (const entry of zipEntries) {
if (entry.entryName === iconPath || entry.entryName.endsWith(iconPath)) { if (entry.fileName === iconPath || entry.fileName.endsWith(iconPath)) {
const data = await entry.getData(); const data = await entry.ReadEntry;
const ext = iconPath.split('.').pop()?.toLowerCase(); const ext = iconPath.split('.').pop()?.toLowerCase();
const mimeType = ext === 'png' ? 'png' : 'jpeg'; const mimeType = ext === 'png' ? 'png' : 'jpeg';

View File

@@ -22,23 +22,18 @@ export class FileExtractor {
const fullPath = path.join(this.modsPath, jarFilename); const fullPath = path.join(this.modsPath, jarFilename);
try { try {
let fileData: Buffer | null = null; const hash = await this.calculateFileHash(fullPath);
try { const mixins = await JarParser.extractMixinsFromFile(fullPath);
fileData = fs.readFileSync(fullPath); const infos = await JarParser.extractModInfoFromFile(fullPath);
const mixins = await JarParser.extractMixins(fileData);
const infos = await JarParser.extractModInfo(fileData);
files.push({ files.push({
filename: fullPath, filename: fullPath,
hash: crypto.createHash('sha1').update(fileData).digest('hex'), hash,
mixins, mixins,
infos, infos,
}); });
logger.debug("文件已处理", { 文件名: fullPath, 绝对路径: path.resolve(fullPath), Mixin数量: mixins.length }); logger.debug("文件已处理", { 文件名: fullPath, 绝对路径: path.resolve(fullPath), Mixin数量: mixins.length });
} finally {
fileData = null;
}
} catch (error: any) { } catch (error: any) {
logger.error("处理文件时出错", { 文件名: fullPath, 错误: error.message }); logger.error("处理文件时出错", { 文件名: fullPath, 错误: error.message });
} }
@@ -48,6 +43,25 @@ export class FileExtractor {
return files; return files;
} }
private async calculateFileHash(filePath: string): Promise<string> {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha1');
const stream = fs.createReadStream(filePath);
stream.on('data', (chunk) => {
hash.update(chunk);
});
stream.on('end', () => {
resolve(hash.digest('hex'));
});
stream.on('error', (error) => {
reject(error);
});
});
}
private getJarFiles(): string[] { private getJarFiles(): string[] {
if (!fs.existsSync(this.modsPath)) { if (!fs.existsSync(this.modsPath)) {
fs.mkdirSync(this.modsPath, { recursive: true }); fs.mkdirSync(this.modsPath, { recursive: true });

View File

@@ -2,7 +2,7 @@ import got, { Got } from "got";
import fs from "node:fs"; import fs from "node:fs";
import fse from "fs-extra"; import fse from "fs-extra";
import { execPromise, fastdownload, version_compare, verifySHA1 } from "../utils/utils.js"; import { execPromise, fastdownload, version_compare, verifySHA1 } from "../utils/utils.js";
import { Azip } from "../utils/ziplib.js"; import { yauzl_promise } from "../utils/ziplib.js";
import { Config } from "../utils/config.js"; import { Config } from "../utils/config.js";
import { logger } from "../utils/logger.js"; import { logger } from "../utils/logger.js";
@@ -84,18 +84,20 @@ export class Forge {
async library() { async library() {
const _downlist: [string, string][] = []; const _downlist: [string, string][] = [];
const data = await fs.promises.readFile(`${this.path}/forge-${this.minecraft}-${this.loaderVersion}-installer.jar`); const data = await fs.promises.readFile(`${this.path}/forge-${this.minecraft}-${this.loaderVersion}-installer.jar`);
const zip = Azip(data); const zip = await yauzl_promise(data);
for await (const entry of zip) { for (const entry of zip) {
if (entry.entryName === "version.json" || entry.entryName === "install_profile.json") { if (entry.fileName === "version.json" || entry.fileName === "install_profile.json") {
JSON.parse((entry.getData()).toString()).libraries.forEach(async (e: any) => { const entryData = await entry.ReadEntry;
JSON.parse(entryData.toString()).libraries.forEach(async (e: any) => {
const t = e.downloads.artifact.path; const t = e.downloads.artifact.path;
_downlist.push([`https://bmclapi2.bangbang93.com/maven/${t}`, `${this.path}/libraries/${t}`]); _downlist.push([`https://bmclapi2.bangbang93.com/maven/${t}`, `${this.path}/libraries/${t}`]);
}); });
} }
if (entry.entryName === "install_profile.json") { if (entry.fileName === "install_profile.json") {
const json = JSON.parse((entry.getData()).toString()) as IForge; const entryData = await entry.ReadEntry;
const json = JSON.parse(entryData.toString()) as IForge;
const vjson = await this.got.get(`version/${this.minecraft}/json`).json<IVersion>(); const vjson = await this.got.get(`version/${this.minecraft}/json`).json<IVersion>();
console.log(`${new URL(vjson.downloads.server_mappings.url).pathname}`); console.log(`${new URL(vjson.downloads.server_mappings.url).pathname}`);
const mojpath = this.MTP(json.data.MOJMAPS.server); const mojpath = this.MTP(json.data.MOJMAPS.server);

View File

@@ -2,7 +2,7 @@ import fs from "node:fs";
import { fastdownload, version_compare } from "../utils/utils.js"; import { fastdownload, version_compare } from "../utils/utils.js";
import got from "got"; import got from "got";
import p from "path"; import p from "path";
import { Azip } from "../utils/ziplib.js"; import { yauzl_promise } from "../utils/ziplib.js";
import { Config } from "../utils/config.js"; import { Config } from "../utils/config.js";
interface ILInfo { interface ILInfo {
@@ -54,12 +54,12 @@ export class Minecraft {
if (version_compare(this.minecraft, "1.18") === 1) { if (version_compare(this.minecraft, "1.18") === 1) {
const mcpath = `${this.path}/libraries/net/minecraft/server/${this.minecraft}/server-${this.minecraft}.jar`; const mcpath = `${this.path}/libraries/net/minecraft/server/${this.minecraft}/server-${this.minecraft}.jar`;
await fastdownload([`https://bmclapi2.bangbang93.com/version/${this.minecraft}/server`, mcpath]); await fastdownload([`https://bmclapi2.bangbang93.com/version/${this.minecraft}/server`, mcpath]);
const zip = await Azip(await fs.promises.readFile(mcpath)); const zip = await yauzl_promise(await fs.promises.readFile(mcpath));
for await (const entry of zip) { for (const entry of zip) {
if (entry.entryName.startsWith("META-INF/libraries/") && !entry.entryName.endsWith("/")) { if (entry.fileName.startsWith("META-INF/libraries/") && !entry.fileName.endsWith("/")) {
console.log(entry.entryName); console.log(entry.fileName);
const data = entry.getData(); const data = await entry.ReadEntry;
const filepath = `${this.path}/libraries/${entry.entryName.replace("META-INF/libraries/", "")}`; const filepath = `${this.path}/libraries/${entry.fileName.replace("META-INF/libraries/", "")}`;
const dir = p.dirname(filepath); const dir = p.dirname(filepath);
await fs.promises.mkdir(dir, { recursive: true }); await fs.promises.mkdir(dir, { recursive: true });
await fs.promises.writeFile(filepath, data); await fs.promises.writeFile(filepath, data);

View File

@@ -1,20 +1,124 @@
import { IInfoFile, IMixinFile } from "../dearth/types.js"; import { IInfoFile, IMixinFile } from "../dearth/types.js";
import { Azip } from "./ziplib.js"; import { yauzl_promise } from "./ziplib.js";
import toml from "smol-toml"; import toml from "smol-toml";
import fs from "node:fs";
import yauzl from "yauzl";
export class JarParser { export class JarParser {
static async extractModInfo(jarData: Buffer): Promise<IInfoFile[]> { static async extractModInfo(jarData: Buffer): Promise<IInfoFile[]> {
return this.extractModInfoFromBuffer(jarData);
}
static async extractMixins(jarData: Buffer): Promise<IMixinFile[]> {
return this.extractMixinsFromBuffer(jarData);
}
static async extractModInfoFromFile(filePath: string): Promise<IInfoFile[]> {
return new Promise((resolve, reject) => {
const infos: IInfoFile[] = []; const infos: IInfoFile[] = [];
const zipEntries = Azip(jarData);
yauzl.open(filePath, (err, zipfile) => {
if (err) {
reject(err);
return;
}
zipfile.on("entry", (entry) => {
if (entry.fileName.endsWith("neoforge.mods.toml") || entry.fileName.endsWith("mods.toml") || entry.fileName.endsWith("fabric.mod.json")) {
zipfile.openReadStream(entry, (err, stream) => {
if (err) {
return;
}
const chunks: Buffer[] = [];
stream.on("data", (chunk) => {
chunks.push(chunk);
});
stream.on("end", () => {
try {
const data = Buffer.concat(chunks);
if (entry.fileName.endsWith(".toml")) {
infos.push({ name: entry.fileName, data: JSON.stringify(toml.parse(data.toString())) });
} else if (entry.fileName.endsWith(".json")) {
infos.push({ name: entry.fileName, data: data.toString() });
}
} catch (error) {
// 忽略解析错误
}
});
});
}
});
zipfile.on("end", () => {
resolve(infos);
});
zipfile.on("error", (err) => {
reject(err);
});
});
});
}
static async extractMixinsFromFile(filePath: string): Promise<IMixinFile[]> {
return new Promise((resolve, reject) => {
const mixins: IMixinFile[] = [];
yauzl.open(filePath, (err, zipfile) => {
if (err) {
reject(err);
return;
}
zipfile.on("entry", (entry) => {
if (entry.fileName.endsWith(".mixins.json") && !entry.fileName.includes("/")) {
zipfile.openReadStream(entry, (err, stream) => {
if (err) {
return;
}
const chunks: Buffer[] = [];
stream.on("data", (chunk) => {
chunks.push(chunk);
});
stream.on("end", () => {
try {
const data = Buffer.concat(chunks);
mixins.push({ name: entry.fileName, data: data.toString() });
} catch (error) {
// 忽略解析错误
}
});
});
}
});
zipfile.on("end", () => {
resolve(mixins);
});
zipfile.on("error", (err) => {
reject(err);
});
});
});
}
private static async extractModInfoFromBuffer(jarData: Buffer): Promise<IInfoFile[]> {
const infos: IInfoFile[] = [];
const zipEntries = await yauzl_promise(jarData);
for (const entry of zipEntries) { for (const entry of zipEntries) {
try { try {
if (entry.entryName.endsWith("neoforge.mods.toml") || entry.entryName.endsWith("mods.toml")) { if (entry.fileName.endsWith("neoforge.mods.toml") || entry.fileName.endsWith("mods.toml")) {
const data = await entry.getData(); const data = await entry.ReadEntry;
infos.push({ name: entry.entryName, data: JSON.stringify(toml.parse(data.toString())) }); infos.push({ name: entry.fileName, data: JSON.stringify(toml.parse(data.toString())) });
} else if (entry.entryName.endsWith("fabric.mod.json")) { } else if (entry.fileName.endsWith("fabric.mod.json")) {
const data = await entry.getData(); const data = await entry.ReadEntry;
infos.push({ name: entry.entryName, data: data.toString() }); infos.push({ name: entry.fileName, data: data.toString() });
} }
} catch (error: any) { } catch (error: any) {
continue; continue;
@@ -24,15 +128,15 @@ export class JarParser {
return infos; return infos;
} }
static async extractMixins(jarData: Buffer): Promise<IMixinFile[]> { private static async extractMixinsFromBuffer(jarData: Buffer): Promise<IMixinFile[]> {
const mixins: IMixinFile[] = []; const mixins: IMixinFile[] = [];
const zipEntries = Azip(jarData); const zipEntries = await yauzl_promise(jarData);
for (const entry of zipEntries) { for (const entry of zipEntries) {
if (entry.entryName.endsWith(".mixins.json") && !entry.entryName.includes("/")) { if (entry.fileName.endsWith(".mixins.json") && !entry.fileName.includes("/")) {
try { try {
const data = await entry.getData(); const data = await entry.ReadEntry;
mixins.push({ name: entry.entryName, data: data.toString() }); mixins.push({ name: entry.fileName, data: data.toString() });
} catch (error: any) { } catch (error: any) {
continue; continue;
} }

View File

@@ -1,4 +1,3 @@
import admZip from "adm-zip";
import yauzl from "yauzl"; import yauzl from "yauzl";
import Stream from "node:stream"; import Stream from "node:stream";
@@ -11,7 +10,7 @@ export async function yauzl_promise(buffer: Buffer): Promise<IentryP[]> {
const zip = await (new Promise((resolve, reject) => { const zip = await (new Promise((resolve, reject) => {
yauzl.fromBuffer( yauzl.fromBuffer(
buffer, buffer,
/*{lazyEntries:true},*/ (err, zipfile) => { (err, zipfile) => {
if (err) { if (err) {
reject(err); reject(err);
return; return;
@@ -43,6 +42,9 @@ export async function yauzl_promise(buffer: Buffer): Promise<IentryP[]> {
stream.on("end", () => { stream.on("end", () => {
resolve(Buffer.concat(chunks)); resolve(Buffer.concat(chunks));
}); });
stream.on("error", (err) => {
reject(err);
});
}); });
}); });
}; };
@@ -64,16 +66,15 @@ export async function yauzl_promise(buffer: Buffer): Promise<IentryP[]> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const entries: IentryP[] = []; const entries: IentryP[] = [];
zip.on("entry", async (entry: yauzl.Entry) => { zip.on("entry", (entry: yauzl.Entry) => {
const entryP = entry as IentryP; const entryP = entry as IentryP;
//console.log(entry.fileName);
entryP.openReadStream = _openReadStream(zip, entry); entryP.openReadStream = _openReadStream(zip, entry);
entryP.ReadEntry = _ReadEntry(zip, entry); entryP.ReadEntry = _ReadEntry(zip, entry);
entries.push(entryP); entries.push(entryP);
if (zip.entryCount === entries.length) { });
zip.on("end", () => {
zip.close(); zip.close();
resolve(entries); resolve(entries);
}
}); });
zip.on("error", (err) => { zip.on("error", (err) => {
reject(err); reject(err);
@@ -81,8 +82,15 @@ export async function yauzl_promise(buffer: Buffer): Promise<IentryP[]> {
}); });
} }
export function Azip(buffer: Buffer) { // 从文件路径打开 zip 文件的函数
const zip = new admZip(buffer); export async function yauzl_from_file(filePath: string): Promise<yauzl.ZipFile> {
const entries = zip.getEntries(); return new Promise((resolve, reject) => {
return entries; yauzl.open(filePath, { lazyEntries: true }, (err, zipfile) => {
if (err) {
reject(err);
return;
}
resolve(zipfile);
});
});
} }

View File

@@ -34,6 +34,7 @@
"@vitejs/plugin-vue": "^5.2.4", "@vitejs/plugin-vue": "^5.2.4",
"typescript": "~5.6.3", "typescript": "~5.6.3",
"vite": "^6.4.1", "vite": "^6.4.1",
"vue-tsc": "^2.2.12" "vue-tsc": "^2.2.12",
"tslib": "^2.8.1"
} }
} }

View File

@@ -1,5 +1,5 @@
{ {
"version": "3.0.35", "version": "3.0.36",
"buildTime": "2026-03-10", "buildTime": "2026-03-15",
"author": "DeEarthX Team" "author": "xcclyc"
} }

View File

@@ -36,7 +36,7 @@ async function getVersionFromJson(): Promise<VersionInfo> {
return { return {
version: '1.0.0', version: '1.0.0',
buildTime: 'Unknown', buildTime: 'Unknown',
author: 'Tianpao' author: 'DeEarthX Team'
}; };
} }
} }

View File

@@ -4,7 +4,7 @@
"description": "main", "description": "main",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.xcclyc.cn/15736060610/DeEarthX-CE.git" "url": "https://git.xcclyc.cn/xcclyc/DeEarthX-CE.git"
}, },
"license": "ISC", "license": "ISC",
"author": "DexV3 Team", "author": "DexV3 Team",

6
pnpm-lock.yaml generated
View File

@@ -166,6 +166,9 @@ importers:
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: ^5.2.4 specifier: ^5.2.4
version: 5.2.4(vite@6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0))(vue@3.5.29(typescript@5.6.3)) version: 5.2.4(vite@6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0))(vue@3.5.29(typescript@5.6.3))
tslib:
specifier: ^2.8.1
version: 2.8.1
typescript: typescript:
specifier: ~5.6.3 specifier: ~5.6.3
version: 5.6.3 version: 5.6.3
@@ -6593,8 +6596,7 @@ snapshots:
babel-jest: 30.2.0(@babel/core@7.29.0) babel-jest: 30.2.0(@babel/core@7.29.0)
jest-util: 30.2.0 jest-util: 30.2.0
tslib@2.8.1: tslib@2.8.1: {}
optional: true
type-detect@4.0.8: {} type-detect@4.0.8: {}