commit adb22f055827d80e348662844ac77fdba3c54015 Author: thzxx <1440196015@qq.com> Date: Sun Aug 23 00:04:16 2026 +0800 v0.1.0: 仙途家族志 · Chronicle of the Immortal Clan 家族模拟器首版:修仙/经营/战斗/外交/叙事全套系统 - Electron + React + TS + MetonaSqlark(aria+OPFS) - 水墨中国风 UI - 引擎种子随机、确定性可回放 - 19 项单元测试、端到端冒烟验证 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d61a57 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +out +dist +release +*.log +.DS_Store diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..4552906 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +registry=https://registry.npmmirror.com +@metona-team:registry=https://git.metona.cn/api/packages/MetonaTeam/npm/ +electron_mirror=https://npmmirror.com/mirrors/electron/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..62c9c0f --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# 仙途家族志 · Chronicle of the Immortal Clan + +修仙 × 家族 × 经营 × 战斗的家族模拟器 0.1.0。 + +## 玩法 + +一座庄园、一家修士、四邻势力,随年月流转:族人修炼突破、建筑产出、探秘寻宝、婚丧嫁娶; +仇家犯境则结阵应战;史书自动记下百年兴衰 —— 玩家扮演「家族意志」,延续香火于仙途。 + +## 开发 + +```bash +npm install # 依赖(含私有 registry:git.metona.cn) +npm run dev # 开发模式(热更新) +npm test # 引擎单元测试(vitest) +npm run typecheck # 类型检查 +npm run build # 构建 out/ +npm run package # 打包桌面安装包(electron-builder) +``` + +冒烟(主进程渲染 + OPFS 数据库就绪检查): + +```bash +npm run build && SMOKE_TEST=1 npx electron out/main/index.js --no-sandbox +``` + +## 技术栈 + +- Electron + electron-vite + React + TypeScript +- 数据库:@metona-team/metona-sqlark 0.7.4(aria 引擎 + OPFS),渲染进程内持久化 +- 引擎:纯 TypeScript(无 UI 依赖),种子随机数(sfc32),确定性可回放 +- 存储:每个存档槽一个 SQLark 库(快照滚动 12 份 + 编年史表),JSON 导出导入 + +## 存档位置 + +OPFS 数据位于 Electron userData 目录下数据库文件(aria 引擎 WAL/SSTable)。 +备份与迁移请用游戏内「设置 → 导出/导入」。 + +## 已知限制(0.1.0) + +- 单机单窗口;MetonaSqlark aria 引擎的多标签页锁不构成影响 +- 战斗为自动结算战报,不做手动回合操作 +- 目前仅中文本地化 diff --git a/electron-builder.yml b/electron-builder.yml new file mode 100644 index 0000000..c6b14b1 --- /dev/null +++ b/electron-builder.yml @@ -0,0 +1,24 @@ +appId: com.metona.cotyc +productName: 仙途家族志 +directories: + output: release + buildResources: resources +files: + - out/** + - package.json +asar: true +npmRebuild: false +win: + target: + - nsis + icon: resources/icon.png + artifactName: ${productName}-${version}-win-${arch}.${ext} +nsis: + oneClick: false + allowToChangeInstallationDirectory: true +linux: + target: + - AppImage + category: Game + maintainer: MetonaTeam + artifactName: ${productName}-${version}-linux-${arch}.${ext} diff --git a/electron.vite.config.ts b/electron.vite.config.ts new file mode 100644 index 0000000..f66a058 --- /dev/null +++ b/electron.vite.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'electron-vite' +import react from '@vitejs/plugin-react' +import { resolve } from 'path' + +export default defineConfig({ + main: {}, + preload: {}, + renderer: { + plugins: [react()], + resolve: { + alias: { + '@': resolve(__dirname, 'src/renderer') + } + } + } +}) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..05f9b29 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7510 @@ +{ + "name": "chronicle-of-the-immortal-clan", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "chronicle-of-the-immortal-clan", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@metona-team/metona-sqlark": "0.7.4" + }, + "devDependencies": { + "@types/node": "^22.10.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "electron": "^33.2.1", + "electron-builder": "^25.1.8", + "electron-vite": "^2.3.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "typescript": "^5.6.3", + "vite": "^5.4.11", + "vitest": "^2.1.8", + "zustand": "^5.0.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmmirror.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmmirror.com/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/notarize/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/notarize/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/@electron/osx-sign/-/osx-sign-1.3.1.tgz", + "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/rebuild": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/@electron/rebuild/-/rebuild-3.6.1.tgz", + "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "node-gyp": "^9.0.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@electron/rebuild/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/rebuild/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/rebuild/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/rebuild/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/universal": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@electron/universal/-/universal-2.0.1.tgz", + "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.2.7", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/universal/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/universal/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/universal/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/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/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@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/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@metona-team/metona-sqlark": { + "version": "0.7.4", + "resolved": "https://git.metona.cn/api/packages/MetonaTeam/npm/%40metona-team%2Fmetona-sqlark/-/0.7.4/metona-sqlark-0.7.4.tgz", + "integrity": "sha512-FgXqhlueMKgZOGl5IWOsIahxv8Cjf61BjDW8184CC3rxesu4F60hW5KQ1B/elpBgt47/0w0+dV7xa1kEtySsjQ==", + "license": "MIT" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.5.tgz", + "integrity": "sha512-jfkGfTwhQpsiSckPF8r9bU3pn3vyd72NlWaO+TgEO6WPSDnUhXzrNYCHBMOYj0ACaUgjm6eERLF+XV9a6RstoA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.5.tgz", + "integrity": "sha512-oGVqyQlxnrz9/ty89oHpU857VUHEl5/Xu4R2lS+aivCTrNnSsbiENzTnNaBsjxH0CNWGPhzHArOLFwo+oKXveA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.5.tgz", + "integrity": "sha512-bW7B8xMEq8n99Q3ieEcPRGuphurdZAaFzQc9Efyyw3FL6DZO6pMy9xhdN+kBoD7Sy05xNXSr4OyPPnpkYriS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.5.tgz", + "integrity": "sha512-YSwBS86QeHOGlrxJ1PSOIZSkzRL/JmKeunhc+lV6M1a6En8QuVCD/T/qIA0J4Gd2Y86RIOBYrLcOUtqGh9+/1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.5.tgz", + "integrity": "sha512-2fST8lILgl7cKbme/1KDdPCmbXbG+gqoV3bHp19L0ypX/3akYMBVdOunPleRCwonoLnXOZ/0F+Mt/v8POFmfcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.5.tgz", + "integrity": "sha512-cpIxQCP9J+EVad0a6LO1kY3ZGODlk80VlI+2I96B8xMcdHZ4pLVhfQ49JFpYqjPF91FFkQWftf57YlDcTiw9yQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.5.tgz", + "integrity": "sha512-r9fGh3eFs3e/udWh5ZjXQtxiYK/xoFxQaYR/cELxac/Udkl5Th+IsFm0CX3Kl9hmUH/we7EoMpjJgeQNnE0+IA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.5.tgz", + "integrity": "sha512-xdvFdp7OM6KLJviJT2g/YuRSUjnZgGHk4RNgwIbN7X6cPugOucV60DdHXWzsBVCUdrGb6qSXnJQrrAKMmQuj3Q==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.5.tgz", + "integrity": "sha512-rRqILAndyzHzP7T9NFQrq+4HFWNhqkqkKur7eiBpfLmz01PO0JKx5Vchu3YllE4YXI/Ftgq/szrDWg5GJ0mI8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.5.tgz", + "integrity": "sha512-Gf4X3qVMucayUvux6aXXPgXovocSFUC0rrffDuPI/S2nHhNMhjcZxsrAFYCOF350PRreW1XwzFj3CT/3bKsWCw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.5.tgz", + "integrity": "sha512-+s5qA0TNM0qm8PK/a5gt/1Hpx+NV08uSuCncvhziIlQzT6AEV2fnUQo7eBtFTFO0nA9scauvoR2HusfXmQnO4w==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.5.tgz", + "integrity": "sha512-ybb6QvWwWJCbBWqERpc8K3pYVGIrXlG8MEQ8IIuJY6Y9KdHQxoFoNyfkAOtKn1VHu3KuLidXvwrvGR1mEjeWCw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.5.tgz", + "integrity": "sha512-nZb1DtnOyhCmYvsC8A2CwOkopVg+IS1+fPUa7rMOAXtNw5+lLCLLPqd6XAiNrGtoQKsbvIBOwsHnBH/3wnb4HQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.5.tgz", + "integrity": "sha512-yMbj63Sp89ryrXLWyz+sy+fYD2HpOnMCLGbe4Oa1smclFSUukdtD/BgdiHaAetJNb74URD8U4hM+qG5KVzMEkg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.5.tgz", + "integrity": "sha512-mhoan3OJw2kYV/e1jtIdmvUZgyBFeA6zGWsOswmR0Tg19TQbowZuR+JMLID6spbbBN7Zee2ejrgmy3+FxGrIdA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.5.tgz", + "integrity": "sha512-5ZTLmjWbb1VZdjuyhe83K/8QO0/h11midQCBP+X5OYn32ra7eOBoM0ZqtaY4nkgNsYgmdVhMYPoyVPTjUpHf3w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.5.tgz", + "integrity": "sha512-m53kG+br6PGxOTmgBEM2DHSDs9RVjsyEbUwjJPJGTFm1grWOG8EKJggDCTb60unD4Tjby8fi7/m9XfkEWasVWg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.5.tgz", + "integrity": "sha512-6RHPJR1g/uvdYU8uXBnfq3nlqyZCP82Fr6NHgfGoaIeSh0YEqnX/x6uA9MmJJbnSH7swqX4F+CkGdUF+6doiQA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.5.tgz", + "integrity": "sha512-xs+OXQtEXgpXT0DmA5+U3qnRZHdCST/5HRQxS8wSPZTUZN/EMWeHuSIod32LQklTBZBV9DyfncKBQ8n5V3eFdw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.5.tgz", + "integrity": "sha512-e7hD+sl3s+mcLQDZ8pbudBVsdG6r5yN4w3LqG2TJ8sQHDpblWj5lrJs/3m01Cvlxbt4x13zu5thLjgypgtkYzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.5.tgz", + "integrity": "sha512-GiyJaCf+WpMub/17aPcKk27QMl5W6f+KhdPTjlFOn5akH5Wa/DCM9Stdx5cDfmasyKB08MqpVQ1uJE2RkkpbXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.5.tgz", + "integrity": "sha512-+OQ8U2DdoEfXl8T4Fb18AjmEwbXMerKDKCL8yCPAYhKCEEKoul7rkbeGCBFCbAlaGaa7pmtRTpkAJM2LE/i5FA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.5.tgz", + "integrity": "sha512-KanvAZrPKbDBFwrgiU9yEVpQoox9QPV1WZOXX7HudJQY+eSlu82CtWxDU8WtuRRvtN5EGkLczkd6Y6DTcvm9wA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.5.tgz", + "integrity": "sha512-1aC3UEWTtRl3RK3VpDJ/Tqk1XI4SLTmXIthAq6wRWo8XiSXJNd+VprJM4/1P4+i6HIaFEFlVi9sTTziniD2tOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.5.tgz", + "integrity": "sha512-/gDJaRs4gl0NPIwqCz+6PkpmhhjRAD2j6P4rSNHBzUkO3naEx2mIU0pRle1vUNRQ7mE/+8OOeXLTv/J56FKiQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.1.tgz", + "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmmirror.com/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.1", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.31", + "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/verror": { + "version": "1.10.11", + "resolved": "https://registry.npmmirror.com/@types/verror/-/verror-1.10.11.tgz", + "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.12", + "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.9.12.tgz", + "integrity": "sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/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/app-builder-bin": { + "version": "5.0.0-alpha.10", + "resolved": "https://registry.npmmirror.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz", + "integrity": "sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib": { + "version": "25.1.8", + "resolved": "https://registry.npmmirror.com/app-builder-lib/-/app-builder-lib-25.1.8.tgz", + "integrity": "sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.1", + "@electron/rebuild": "3.6.1", + "@electron/universal": "2.0.1", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "chromium-pickle-js": "^0.2.0", + "config-file-ts": "0.2.8-rc1", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "ejs": "^3.1.8", + "electron-publish": "25.1.7", + "form-data": "^4.0.0", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "is-ci": "^3.0.0", + "isbinaryfile": "^5.0.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "lazy-val": "^1.0.5", + "minimatch": "^10.0.0", + "resedit": "^1.7.0", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.8", + "tar": "^6.1.12", + "temp-file": "^3.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "25.1.8", + "electron-builder-squirrel-windows": "25.1.8" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "dev": true, + "license": "ISC" + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/baseline-browser-mapping": { + "version": "2.11.18", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.18.tgz", + "integrity": "sha512-1iEmLEYSiE1SeBoAfPo/Mnx3PzfzHUkDK61ASkCpuk3YXugYLH5DYK1SzqV55F8FMI6s0F+/tCP7Polz1QRjxw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/bluebird-lst": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz", + "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.5.5" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.8", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "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.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builder-util": { + "version": "25.1.7", + "resolved": "https://registry.npmmirror.com/builder-util/-/builder-util-25.1.7.tgz", + "integrity": "sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "5.0.0-alpha.10", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "9.2.10", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "is-ci": "^3.0.0", + "js-yaml": "^4.1.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.2.10", + "resolved": "https://registry.npmmirror.com/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz", + "integrity": "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/builder-util/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/builder-util/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/builder-util/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmmirror.com/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/cacache/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmmirror.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmmirror.com/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001809", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", + "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", + "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/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmmirror.com/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/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/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-file-ts": { + "version": "0.2.8-rc1", + "resolved": "https://registry.npmmirror.com/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", + "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.3.12", + "typescript": "^5.4.3" + } + }, + "node_modules/config-file-ts/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-file-ts/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/config-file-ts/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmmirror.com/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/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/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dir-compare": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "node_modules/dir-compare/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dmg-builder": { + "version": "25.1.8", + "resolved": "https://registry.npmmirror.com/dmg-builder/-/dmg-builder-25.1.8.tgz", + "integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "25.1.8", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "node_modules/dmg-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dmg-builder/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/dmg-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmmirror.com/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "bin": { + "dmg-license": "bin/dmg-license.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "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/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "33.4.11", + "resolved": "https://registry.npmmirror.com/electron/-/electron-33.4.11.tgz", + "integrity": "sha512-xmdAs5QWRkInC7TpXGNvzo/7exojubk+72jn1oJL7keNeIlw7xNglf8TGtJtkR4rWC5FJq0oXiIXPS9BcK2Irg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron-builder": { + "version": "25.1.8", + "resolved": "https://registry.npmmirror.com/electron-builder/-/electron-builder-25.1.8.tgz", + "integrity": "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "25.1.8", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "chalk": "^4.1.2", + "dmg-builder": "25.1.8", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", + "lazy-val": "^1.0.5", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "25.1.8", + "resolved": "https://registry.npmmirror.com/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.8.tgz", + "integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "25.1.8", + "archiver": "^5.3.1", + "builder-util": "25.1.7", + "fs-extra": "^10.1.0" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-publish": { + "version": "25.1.7", + "resolved": "https://registry.npmmirror.com/electron-publish/-/electron-publish-25.1.7.tgz", + "integrity": "sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-publish/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-publish/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-publish/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.412", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.412.tgz", + "integrity": "sha512-z4rMe3esBzlzovKHj4gxJnsCGZRK5l4baUvm+gCGJBPE+gsyUMKsuU9tnEUtI1dOebXz1ytAPGjvXhmQ7rIPwA==", + "dev": true, + "license": "ISC" + }, + "node_modules/electron-vite": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/electron-vite/-/electron-vite-2.3.0.tgz", + "integrity": "sha512-lsN2FymgJlp4k6MrcsphGqZQ9fKRdJKasoaiwIrAewN1tapYI/KINLdfEL7n10LuF0pPSNf/IqjzZbB5VINctg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.7", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "cac": "^6.7.14", + "esbuild": "^0.21.5", + "magic-string": "^0.30.10", + "picocolors": "^1.0.1" + }, + "bin": { + "electron-vite": "bin/electron-vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@swc/core": "^1.0.0", + "vite": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + } + } + }, + "node_modules/electron/node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "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/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/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/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/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-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "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.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmmirror.com/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + }, + "engines": { + "node": "^8.11.2 || >=10" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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": "BSD-3-Clause" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.5.0", + "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.5.0.tgz", + "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/isbinaryfile": { + "version": "5.0.7", + "resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-5.0.7.tgz", + "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmmirror.com/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/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/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/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/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/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.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmmirror.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/minipass-flush/-/minipass-flush-1.0.7.tgz", + "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.94.0", + "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.94.0.tgz", + "integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-api-version/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp": { + "version": "9.4.1", + "resolved": "https://registry.npmmirror.com/node-gyp/-/node-gyp-9.4.1.tgz", + "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-releases": { + "version": "2.0.53", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/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-map": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/plist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/plist/-/plist-3.1.1.tgz", + "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "bin": { + "read-binary-file-arch": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmmirror.com/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/rollup": { + "version": "4.62.5", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.62.5.tgz", + "integrity": "sha512-/tqMfgP7GPA3PHhCmuiS4vIjrSVhHLgY++i+dhbG462euyAj7FpM4D9uq1X3BgjlqRdpcOrYhcQtfiQLNc8tqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.5", + "@rollup/rollup-android-arm64": "4.62.5", + "@rollup/rollup-darwin-arm64": "4.62.5", + "@rollup/rollup-darwin-x64": "4.62.5", + "@rollup/rollup-freebsd-arm64": "4.62.5", + "@rollup/rollup-freebsd-x64": "4.62.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.5", + "@rollup/rollup-linux-arm-musleabihf": "4.62.5", + "@rollup/rollup-linux-arm64-gnu": "4.62.5", + "@rollup/rollup-linux-arm64-musl": "4.62.5", + "@rollup/rollup-linux-loong64-gnu": "4.62.5", + "@rollup/rollup-linux-loong64-musl": "4.62.5", + "@rollup/rollup-linux-ppc64-gnu": "4.62.5", + "@rollup/rollup-linux-ppc64-musl": "4.62.5", + "@rollup/rollup-linux-riscv64-gnu": "4.62.5", + "@rollup/rollup-linux-riscv64-musl": "4.62.5", + "@rollup/rollup-linux-s390x-gnu": "4.62.5", + "@rollup/rollup-linux-x64-gnu": "4.62.5", + "@rollup/rollup-linux-x64-musl": "4.62.5", + "@rollup/rollup-openbsd-x64": "4.62.5", + "@rollup/rollup-openharmony-arm64": "4.62.5", + "@rollup/rollup-win32-arm64-msvc": "4.62.5", + "@rollup/rollup-win32-ia32-msvc": "4.62.5", + "@rollup/rollup-win32-x64-gnu": "4.62.5", + "@rollup/rollup-win32-x64-msvc": "4.62.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.4", + "resolved": "https://registry.npmmirror.com/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmmirror.com/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/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/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/temp-file/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/temp-file/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", + "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.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmmirror.com/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/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/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/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/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zustand": { + "version": "5.0.15", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.15.tgz", + "integrity": "sha512-MpSEjRiBkA9crSYeOUH32rJC7SVqAbm0Fqcqge/bUi2PPoLcBWKOsG+C8mevmpr8TwXHBVkChbbJiyvkE+i/3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d73abf2 --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "chronicle-of-the-immortal-clan", + "productName": "仙途家族志", + "version": "0.1.0", + "description": "修仙 · 家族 · 经营 · 战斗 模拟器", + "main": "./out/main/index.js", + "author": "MetonaTeam", + "license": "MIT", + "private": true, + "scripts": { + "dev": "electron-vite dev", + "build": "electron-vite build", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "package": "npm run build && electron-builder" + }, + "dependencies": { + "@metona-team/metona-sqlark": "0.7.4" + }, + "devDependencies": { + "@types/node": "^22.10.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "electron": "^33.2.1", + "electron-builder": "^25.1.8", + "electron-vite": "^2.3.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "typescript": "^5.6.3", + "vite": "^5.4.11", + "vitest": "^2.1.8", + "zustand": "^5.0.2" + }, + "allowScripts": { + "electron@33.4.11": true, + "esbuild@0.21.5": true + } +} diff --git a/resources/icon.ico b/resources/icon.ico new file mode 100644 index 0000000..da57378 Binary files /dev/null and b/resources/icon.ico differ diff --git a/resources/icon.png b/resources/icon.png new file mode 100644 index 0000000..b742259 Binary files /dev/null and b/resources/icon.png differ diff --git a/src/main/index.ts b/src/main/index.ts new file mode 100644 index 0000000..3365d2c --- /dev/null +++ b/src/main/index.ts @@ -0,0 +1,201 @@ +import { app, BrowserWindow, ipcMain, dialog, shell, protocol, net } from 'electron' +import { join, normalize, sep } from 'path' +import { readFileSync, writeFileSync } from 'fs' +import { pathToFileURL } from 'url' + +const SCHEME = 'app' +const RENDERER_ROOT = join(__dirname, '../renderer') + +protocol.registerSchemesAsPrivileged([ + { + scheme: SCHEME, + privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true } + } +]) + +let mainWindow: BrowserWindow | null = null + +function createWindow(): void { + mainWindow = new BrowserWindow({ + width: 1440, + height: 900, + minWidth: 1120, + minHeight: 720, + show: false, + autoHideMenuBar: true, + backgroundColor: '#191512', + title: '仙途家族志', + webPreferences: { + preload: join(__dirname, '../preload/index.js'), + sandbox: false, + contextIsolation: true, + nodeIntegration: false + } + }) + + const iconPath = join(__dirname, '../../resources/icon.png') + try { + readFileSync(iconPath) + mainWindow.setIcon(iconPath) + } catch { + // no icon in dev context; packaged builds use the builder icon + } + + mainWindow.on('ready-to-show', () => { + mainWindow?.show() + }) + + if (process.env.SMOKE_TEST) { + const wc = mainWindow.webContents + const shotsDir = process.env.SMOKE_SHOTS_DIR + wc.on('console-message', (_e, level, message) => { + console.log(`[renderer:${level}] ${message}`) + }) + wc.on('did-finish-load', async () => { + try { + const title = await wc.executeJavaScript('document.title') + let bootReady = 0 + for (let i = 0; i < 40; i++) { + bootReady = await wc.executeJavaScript( + "Number(window.__cotycBootReady ?? 0)" + ) + if (bootReady === 1) break + await new Promise((r) => setTimeout(r, 250)) + } + const rootLen = await wc.executeJavaScript( + "document.getElementById('root')?.children.length ?? -1" + ) + let smoke2 = 'skipped' + let shotCount = 0 + if (bootReady === 1 && rootLen > 0) { + if (shotsDir) { + await new Promise((r) => setTimeout(r, 900)) + await capture(wc, shotsDir, 'boot') + shotCount++ + } + smoke2 = await (async () => { + await wc.executeJavaScript('window.__cotycDebug.startNewGame()') + await new Promise((r) => setTimeout(r, 600)) + for (let i = 0; i < 34; i++) { + await wc.executeJavaScript('window.__cotycDebug.advance()') + await wc.executeJavaScript('window.__cotycDebug.resolvePending()') + } + await new Promise((r) => setTimeout(r, 500)) + await wc.executeJavaScript('window.__cotycDebug.closeBattles()') + if (shotsDir) { + await capture(wc, shotsDir, 'game-family') + shotCount++ + await wc.executeJavaScript('window.__cotycDebug.openPanel("territory")') + await new Promise((r) => setTimeout(r, 400)) + await capture(wc, shotsDir, 'game-territory') + shotCount++ + await wc.executeJavaScript('window.__cotycDebug.openPanel("market")') + await new Promise((r) => setTimeout(r, 400)) + await capture(wc, shotsDir, 'game-market') + shotCount++ + await wc.executeJavaScript('window.__cotycDebug.openPanel("chronicle")') + await new Promise((r) => setTimeout(r, 400)) + await capture(wc, shotsDir, 'game-chronicle') + shotCount++ + } + const year = (await wc.executeJavaScript('window.__cotycDebug.getYear()')) as number + const famName = await wc.executeJavaScript( + "document.querySelector('.fam-name')?.textContent ?? ''" + ) + return `year=${year} fam=${famName} shots=${shotCount}` + })() + } + console.log(`[SMOKE] title=${title} rootChildren=${rootLen} bootReady=${bootReady} game=${smoke2}`) + app.exit(bootReady === 1 && rootLen > 0 ? 0 : 1) + } catch (e) { + console.error('[SMOKE] failed', e) + app.exit(1) + } + }) + } + + mainWindow.webContents.setWindowOpenHandler((details) => { + void shell.openExternal(details.url) + return { action: 'deny' } + }) + + if (process.env.ELECTRON_RENDERER_URL) { + void mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL) + } else { + void mainWindow.loadURL(`app://bundle/index.html`) + } +} + +async function capture( + wc: Electron.WebContents, + dir: string, + name: string +): Promise { + const image = await wc.capturePage() + const buf = image.toPNG() + const { mkdirSync, writeFileSync } = await import('fs') + const { join } = await import('path') + mkdirSync(dir, { recursive: true }) + writeFileSync(join(dir, `${name}.png`), buf) + console.log(`[SHOT] ${dir}/${name}.png`) +} + +function serveAppProtocol(): void { + protocol.handle(SCHEME, (req) => { + const url = new URL(req.url) + let pathname = decodeURIComponent(url.pathname) + if (pathname === '/') pathname = '/index.html' + const target = normalize(join(RENDERER_ROOT, pathname)) + sep + const resolved = normalize(join(RENDERER_ROOT, pathname)) + const rootPrefix = normalize(RENDERER_ROOT) + sep + const safe = resolved.startsWith(rootPrefix) || resolved === normalize(RENDERER_ROOT) + if (!safe) { + return new Response('forbidden', { status: 403 }) + } + return net.fetch(pathToFileURL(resolved).toString()) + }) +} + +app.whenReady().then(() => { + serveAppProtocol() + createWindow() + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) createWindow() + }) +}) + +app.on('window-all-closed', () => { + app.quit() +}) + +ipcMain.handle('save:export', async (_ev, json: string, defaultName: string) => { + const win = BrowserWindow.getFocusedWindow() ?? mainWindow + const result = await dialog.showSaveDialog(win!, { + title: '导出存档', + defaultPath: `${defaultName}.json`, + filters: [{ name: '存档文件', extensions: ['json'] }] + }) + if (result.canceled || !result.filePath) return { ok: false as const } + try { + writeFileSync(result.filePath, json, 'utf-8') + return { ok: true as const } + } catch (e) { + return { ok: false as const, error: String(e) } + } +}) + +ipcMain.handle('save:import', async () => { + const win = BrowserWindow.getFocusedWindow() ?? mainWindow + const result = await dialog.showOpenDialog(win!, { + title: '导入存档', + filters: [{ name: '存档文件', extensions: ['json'] }], + properties: ['openFile'] + }) + if (result.canceled || result.filePaths.length === 0) return { ok: false as const } + try { + const text = readFileSync(result.filePaths[0], 'utf-8') + return { ok: true as const, text } + } catch (e) { + return { ok: false as const, error: String(e) } + } +}) diff --git a/src/preload/index.ts b/src/preload/index.ts new file mode 100644 index 0000000..036afab --- /dev/null +++ b/src/preload/index.ts @@ -0,0 +1,21 @@ +import { contextBridge, ipcRenderer } from 'electron' + +export interface SaveExportResult { + ok: boolean + error?: string +} +export interface SaveImportResult { + ok: boolean + text?: string + error?: string +} + +const api = { + exportSave: (json: string, defaultName: string): Promise => + ipcRenderer.invoke('save:export', json, defaultName), + importSave: (): Promise => ipcRenderer.invoke('save:import') +} + +contextBridge.exposeInMainWorld('api', api) + +export type Api = typeof api diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx new file mode 100644 index 0000000..d87da88 --- /dev/null +++ b/src/renderer/App.tsx @@ -0,0 +1,27 @@ +import { useEffect } from 'react' +import { useGameStore } from './ui/store' +import Boot from './ui/screens/Boot' +import NewGame from './ui/screens/NewGame' +import GameScreen from './ui/screens/GameScreen' +import { EventModal } from './ui/components/EventModal' +import { BattleModal } from './ui/components/BattleModal' + +export default function App() { + const screen = useGameStore((s) => s.screen) + const pendingEventId = useGameStore((s) => s.pendingEventId) + const battleView = useGameStore((s) => s.battleView) + + useEffect(() => { + useGameStore.getState().init() + }, []) + + return ( +
+ {screen === 'boot' && } + {screen === 'newgame' && } + {screen === 'game' && } + {pendingEventId && } + {battleView && } +
+ ) +} diff --git a/src/renderer/assets/mountain.svg b/src/renderer/assets/mountain.svg new file mode 100644 index 0000000..a5e7a44 --- /dev/null +++ b/src/renderer/assets/mountain.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/renderer/env.d.ts b/src/renderer/env.d.ts new file mode 100644 index 0000000..ff25272 --- /dev/null +++ b/src/renderer/env.d.ts @@ -0,0 +1,6 @@ +interface Window { + api?: { + exportSave: (json: string, defaultName: string) => Promise<{ ok: boolean; error?: string }> + importSave: () => Promise<{ ok: boolean; text?: string; error?: string }> + } +} diff --git a/src/renderer/game/core/names.ts b/src/renderer/game/core/names.ts new file mode 100644 index 0000000..abd7579 --- /dev/null +++ b/src/renderer/game/core/names.ts @@ -0,0 +1,30 @@ +export const SURNAME_POOL = [ + '林', '苏', '沈', '谢', '顾', '萧', '叶', '江', '秦', '裴', + '柳', '陆', '云', '姜', '晏', '楚', '洛', '许', '宋', '薛', + '韩', '白', '纪', '容', '卫', '柳', '燕', '温', '孟', '阮', + '洛', '池', '顾', '岑', '傅', '虞', '尹', '霍', '曲', '齐' +] + +export const MALE_GIVEN = [ + '长青', '天行', '玄机', '惊羽', '逐月', '凌风', '无涯', '浩然', '慕白', '景行', + '玄青', '清尘', '衡之', '元白', '既白', '拾遗', '忘机', '观澜', '承影', '风眠', + '听澜', '常宁', '若拙', '怀瑾', '望舒', '断岳', '疏影', '临渊', '未央', '扶摇', + '云疏', '星河', '知竹', '不归', '须眉', '长恨', '夜阑', '青崖', '子衿', '方舟', + '晓风', '修远', '行舟', '暮雪', '佐卿', '御风', '惊蛰', '白露', '秋声', '冬青' +] + +export const FEMALE_GIVEN = [ + '青鸾', '月瑶', '星若', '清欢', '婉芯', '若雪', '凝霜', '沐婉', '疏桐', '归鹤', + '临夏', '小满', '云笙', '芷若', '洛神', '巧儿', '霜华', '雨薇', '兰心', '扶摇', + '晴雪', '芷凝', '吟月', '采薇', '爱雪', '听雨', '素问', '千雪', '疏影', '知微', + '曼卿', '昭容', '碧衣', '青眉', '玲珑', '止水', '言心', '灼华', '云鬓', '惊鸿', + '晚棠', '夜莺', '南絮', '海棠', '锦瑟', '泠泠', '天心', '妙音', '含章', '碧瑶' +] + +export const MISSION_DESCRIPTORS = [ + '顽劣', '沉静', '木讷', '机敏', '温厚', '孤高', '谦逊', '豪迈', '精细', '洒脱' +] + +export function randomSurname(rng: { pick(a: T[]): T }): string { + return rng.pick(SURNAME_POOL) +} diff --git a/src/renderer/game/core/rng.ts b/src/renderer/game/core/rng.ts new file mode 100644 index 0000000..ce5a639 --- /dev/null +++ b/src/renderer/game/core/rng.ts @@ -0,0 +1,76 @@ +import type { RngState } from '../types/domain' + +function xmur3(str: string): () => number { + let h = 1779033703 ^ str.length + for (let i = 0; i < str.length; i++) { + h = Math.imul(h ^ str.charCodeAt(i), 3432918353) + h = (h << 13) | (h >>> 19) + } + return function () { + h = Math.imul(h ^ (h >>> 16), 2246822507) + h = Math.imul(h ^ (h >>> 13), 3266489909) + return (h ^= h >>> 16) >>> 0 + } +} + +export function seedToRng(seed: string): RngState { + const s = xmur3(seed) + let a = s() + let b = s() + let c = s() + let d = s() + if (a === 0 && b === 0 && c === 0 && d === 0) d = 0x9e3779b9 + return { a, b, c, d } +} + +const U32 = 4294967296 + +export class Rng { + state: RngState + + constructor(state: RngState) { + this.state = { ...state } + } + + getState(): RngState { + return { ...this.state } + } + + next(): number { + const s = this.state + const t = ((s.a + s.b + s.d) | 0) >>> 0 + s.d = (s.d + 1) | 0 + s.a = (s.b ^ (s.b >>> 9)) >>> 0 + s.b = (s.c + (s.c << 3)) | 0 + s.c = ((s.c << 21) | (s.c >>> 11)) >>> 0 + s.c = (s.c + t) | 0 + return t / U32 + } + + int(min: number, max: number): number { + return Math.floor(this.next() * (max - min + 1)) + min + } + + pick(arr: T[]): T { + return arr[this.int(0, arr.length - 1)] + } + + chance(p: number): boolean { + return this.next() < p + } + + shuffle(arr: T[]): T[] { + const a = [...arr] + for (let i = a.length - 1; i > 0; i--) { + const j = this.int(0, i) + const t = a[i] + a[i] = a[j] + a[j] = t + } + return a + } + + between(min: number, max: number): number { + return this.next() * (max - min) + min + } +} diff --git a/src/renderer/game/data/buildings.ts b/src/renderer/game/data/buildings.ts new file mode 100644 index 0000000..d6a012a --- /dev/null +++ b/src/renderer/game/data/buildings.ts @@ -0,0 +1,97 @@ +export interface BuildingDef { + id: string + name: string + icon: string + desc: string + kind: 'produce' | 'function' + maxLevel: number + produceTable?: (level: number) => { stone?: number; lingcao?: number; lingkuang?: number; beastcore?: number } + upgradeCost: (level: number) => { stones: number; lingkuang: number } + extra?: Record +} + +export const BUILDINGS: Record = { + lingtian: { + id: 'lingtian', name: '灵田', icon: '田', + desc: '种植灵草,每月产出稳定。', + kind: 'produce', maxLevel: 5, + produceTable: (l) => ({ lingcao: 10 * l }), + upgradeCost: (l) => ({ stones: 80 * Math.pow(1.7, l - 1), lingkuang: 12 * l }) + }, + yaoyuan: { + id: 'yaoyuan', name: '药园', icon: '药', + desc: '培育稀有药草以炼丹药。', + kind: 'produce', maxLevel: 5, + produceTable: (l) => ({ lingcao: 5 * l, beastcore: l >= 3 ? 1 : 0 }), + upgradeCost: (l) => ({ stones: 110 * Math.pow(1.7, l - 1), lingkuang: 15 * l }) + }, + lingkuang: { + id: 'lingkuang', name: '灵矿', icon: '矿', + desc: '开采灵石矿脉,供给炼器布阵。', + kind: 'produce', maxLevel: 5, + produceTable: (l) => ({ lingkuang: 8 * l }), + upgradeCost: (l) => ({ stones: 130 * Math.pow(1.7, l - 1), lingkuang: 10 * l }) + }, + fangshi: { + id: 'fangshi', name: '坊市', icon: '市', + desc: '设摊售货,每月进账灵石。', + kind: 'produce', maxLevel: 5, + produceTable: (l) => ({ stone: 55 * l }), + upgradeCost: (l) => ({ stones: 150 * Math.pow(1.7, l - 1), lingkuang: 20 * l }) + }, + danfang: { + id: 'danfang', name: '丹房', icon: '丹', + desc: '炼制丹药,等级越高成丹率越高。', + kind: 'function', maxLevel: 5, + upgradeCost: (l) => ({ stones: 170 * Math.pow(1.7, l - 1), lingkuang: 18 * l }), + extra: { craftChance: '0.5 + 0.1*L' } + }, + cangshu: { + id: 'cangshu', name: '藏书阁', icon: '书', + desc: '收藏功法典籍,等级越高越能寻得宝典。', + kind: 'function', maxLevel: 5, + upgradeCost: (l) => ({ stones: 160 * Math.pow(1.7, l - 1), lingkuang: 12 * l }), + extra: { unlockGrade: '1 + L' } + }, + juling: { + id: 'juling', name: '聚灵阵', icon: '阵', + desc: '汇聚天地灵气,全族修炼加成。', + kind: 'function', maxLevel: 5, + upgradeCost: (l) => ({ stones: 200 * Math.pow(1.7, l - 1), lingkuang: 30 * l }), + extra: { expBonus: '0.05 * L' } + }, + yanwu: { + id: 'yanwu', name: '演武场', icon: '武', + desc: '操练武艺,家族战力加成。', + kind: 'function', maxLevel: 5, + upgradeCost: (l) => ({ stones: 140 * Math.pow(1.7, l - 1), lingkuang: 25 * l }), + extra: { powerBonus: '0.04 * L' } + }, + dongfu: { + id: 'dongfu', name: '洞府', icon: '府', + desc: '闭关修行之所,闭关者修为加成。', + kind: 'function', maxLevel: 5, + upgradeCost: (l) => ({ stones: 220 * Math.pow(1.7, l - 1), lingkuang: 35 * l }), + extra: { expBonus: '0.08 * L' } + }, + zongci: { + id: 'zongci', name: '宗祠', icon: '祠', + desc: '供奉先祖,家族声望与生育兴旺。', + kind: 'function', maxLevel: 5, + upgradeCost: (l) => ({ stones: 120 * Math.pow(1.7, l - 1), lingkuang: 8 * l }), + extra: { repBonus: '0.3 * L /年' } + }, + lingshou: { + id: 'lingshou', name: '灵兽园', icon: '兽', + desc: '豢养灵兽,战阵之助,偶得兽核。', + kind: 'function', maxLevel: 5, + upgradeCost: (l) => ({ stones: 190 * Math.pow(1.7, l - 1), lingkuang: 28 * l }), + extra: { powerBonus: '0.05 * L' } + } +} + +export const BUILDING_IDS = Object.keys(BUILDINGS) + +export function buildingById(id: string): BuildingDef { + return BUILDINGS[id] +} diff --git a/src/renderer/game/data/elements.ts b/src/renderer/game/data/elements.ts new file mode 100644 index 0000000..b7d9477 --- /dev/null +++ b/src/renderer/game/data/elements.ts @@ -0,0 +1,27 @@ +import { Element } from '../types/domain' + +export const ELEMENT_LIST: Element[] = ['金', '木', '水', '火', '土'] + +export const ROOT_GRADE_NAMES = ['伪灵根', '凡品灵根', '玄品灵根', '真品灵根', '天品灵根', '仙品灵根'] + +export const ROOT_GRADE_COLORS = ['#8a8078', '#9cb27c', '#7fa7c9', '#b08cd9', '#e6b84c', '#e05a5a'] + +export interface RootGradeDef { + name: string + expBonus: number + drawWeight: number +} + +export const ROOT_GRADES: Record = { + 0: { name: '伪灵根', expBonus: 0.5, drawWeight: 14 }, + 1: { name: '凡品灵根', expBonus: 0.8, drawWeight: 38 }, + 2: { name: '玄品灵根', expBonus: 1.0, drawWeight: 30 }, + 3: { name: '真品灵根', expBonus: 1.3, drawWeight: 12 }, + 4: { name: '天品灵根', expBonus: 1.7, drawWeight: 5 }, + 5: { name: '仙品灵根', expBonus: 2.2, drawWeight: 1.5 } +} + +export function describeRoots(roots: { grade: number; primary: Element; secondary: Element[] }): string { + const parts = [roots.primary, ...roots.secondary].join('') + return `${ROOT_GRADE_NAMES[roots.grade] ?? '伪灵根'}(${parts})` +} diff --git a/src/renderer/game/data/events.ts b/src/renderer/game/data/events.ts new file mode 100644 index 0000000..878a732 --- /dev/null +++ b/src/renderer/game/data/events.ts @@ -0,0 +1,305 @@ +export interface Cond { + all?: Cond[] + any?: Cond[] + not?: Cond + minYear?: number + minGeneration?: number + minHeadRealm?: string + minBuilding?: { id: string; level: number } + minRep?: number + maxRep?: number + minResource?: { id: string; n: number } + minAdult?: number + maxAdult?: number + minMembers?: number + eligibleAdult?: number + relation?: { npcId: string; gt?: number; lt?: number } + flag?: { key: string; eq: number | boolean | string } + hasMeditation?: boolean + noWarForYears?: number + minTechCount?: number +} + +export interface MemberEffect { + by: 'exp' | 'wound' | 'heal' | 'breakthrough' | 'fatal' | 'repGain' | 'inspire' | 'loot' | 'madness' | 'genius' + target: 'random' | 'head' | 'youngest' | 'oldest' | 'highestPerception' | 'highestPower' | 'highestFortune' | 'all' + n?: number + desc?: string +} + +export interface EffectDef { + res?: Record + rep?: number + relation?: Record + addBuilding?: string + memberBy?: MemberEffect + pillGain?: Record + mission?: string + raid?: { npcId: string } + flag?: Record + techniqueChance?: number + artifactChance?: number + addTech?: string +} + +export interface EventOptionDef { + label: string + hint?: string + eff: EffectDef + note?: string +} + +export interface EventDef { + id: string + name: string + category: 'daily' | 'major' | 'fate' + weight: number + once?: boolean + cond?: Cond + text: string + options: EventOptionDef[] +} + +export function eventCategoryName(cat: EventDef['category']): string { + return cat === 'daily' ? '族中日常' : cat === 'major' ? '家国大事' : '乾坤造化' +} + +export const EVENTS: EventDef[] = [ + { + id: 'ev-youdao', name: '云游道人', category: 'daily', weight: 8, + text: '一名袖袍破旧的云游道人路过祖宅门前,自称识得丹药机巧,愿以一枚聚气丹换五斤灵草。', + options: [ + { label: '换!', hint: '灵草-5,聚气丹+1', eff: { res: { lingcao: -5 }, pillGain: { 'pill-qiyuan': 1 } } }, + { label: '送他些斋饭', hint: '灵石-10,声望+2', eff: { res: { stones: -10 }, rep: 2 } }, + { label: '赶走', hint: '无', eff: {} } + ] + }, + { + id: 'ev-chonghai', name: '灵田虫害', category: 'daily', weight: 8, + cond: { minBuilding: { id: 'lingtian', level: 1 } }, + text: '成片的灵草被青翅虫啃食,照此下去今季收成不保。', + options: [ + { label: '请灵师除虫', hint: '灵石-60', eff: { res: { stones: -60 } } }, + { label: '亲自下田燃烧草', hint: '收成-40%', eff: { res: { lingcao: -8 } } }, + { label: '冷眼旁观', hint: '灵草-20', eff: { res: { lingcao: -20 } } } + ] + }, + { + id: 'ev-liumin', name: '流民投奔', category: 'daily', weight: 6, + cond: { minRep: 0 }, + text: '乱世流民逃难至此,成群跪在庄外,望收留他们做佃户。', + options: [ + { label: '开仓设粥', hint: '灵石-40,声望+4', eff: { res: { stones: -40 }, rep: 4 } }, + { label: '遣散', hint: '声望-2', eff: { rep: -2 } }, + { label: '收为佃户', hint: '灵田产量长增', eff: { res: { stones: -30 }, rep: 2, flag: { tenants: true } } } + ] + }, + { + id: 'ev-takuang', name: '后山塌方', category: 'daily', weight: 6, + cond: { minBuilding: { id: 'lingkuang', level: 1 } }, + text: '后山矿洞一段塌方,矿工数人被困,矿脉暂无人敢下。', + options: [ + { label: '出灵石救治', hint: '灵石-50', eff: { res: { stones: -50 } } }, + { label: '调族人救援', hint: '随机成员负伤', eff: { memberBy: { by: 'wound', target: 'random', n: 15 } } }, + { label: '封洞了事', hint: '矿脉-5', eff: { res: { lingkuang: -5 } } } + ] + }, + { + id: 'ev-lieshou', name: '猎户献兽', category: 'daily', weight: 5, + cond: { minRep: 10 }, + text: '山中猎户捉得一头灵鹿,闻贵族历代尚灵,特来献上。', + options: [ + { label: '买下放生', hint: '灵石-20', eff: { res: { stones: -20 } } }, + { label: '收为坐骑', hint: '兽核+2', eff: { res: { beastcore: 2 } } }, + { label: '收他入庄', hint: '声望+1', eff: { rep: 1 } } + ] + }, + { + id: 'ev-jiangjiang', name: '巧匠上门', category: 'daily', weight: 5, + cond: { minResource: { id: 'stones', n: 20 } }, + text: '一位独臂巧匠携着琳琅法器上门求售,说是祖传手笔。', + options: [ + { label: '买一件回头客', hint: '灵石-100,凡器+1', eff: { res: { stones: -100 }, pillGain: { 'weapon-fan': 1 } } }, + { label: '替他说媒去', hint: '声望+2', eff: { rep: 2 } }, + { label: '让他留个住址', hint: '无', eff: {} } + ] + }, + { + id: 'ev-lingyu', name: '灵雨', category: 'daily', weight: 6, + cond: { minBuilding: { id: 'lingtian', level: 1 } }, + text: '一夜灵雨滂沱,田里的灵草吸饱了灵雨,长势惊人。', + options: [ + { label: '天佑我家!', hint: '灵草+60', eff: { res: { lingcao: 60 } } } + ] + }, + { + id: 'ev-yupei', name: '孩童拾玉', category: 'daily', weight: 5, + text: '族中孩童在后山拾到一枚玄玉,触手生温,似有不凡。', + options: [ + { label: '供奉宗祠', hint: '声望+5', eff: { rep: 5, flag: { jade: true } } }, + { label: '锁入库房', hint: '灵石+80', eff: { res: { stones: 80 } } }, + { label: '让孩童留之', hint: '其气运+2', eff: { memberBy: { by: 'loot', target: 'youngest', n: 2 }, flag: { jade2: true } } } + ] + }, + { + id: 'ev-shangdui', name: '路过商队', category: 'daily', weight: 6, + text: '一支四海王家的商队路过,问庄上可有灵矿出手,价给得公道。', + options: [ + { label: '出货', hint: '灵矿-15,灵石+240', eff: { res: { lingkuang: -15, stones: 240 } } }, + { label: '请他们留宿', hint: '声望+2', eff: { rep: 2 } }, + { label: '不理', hint: '无', eff: {} } + ] + }, + { + id: 'ev-zhusu', name: '祝氏家奴', category: 'daily', weight: 5, + cond: { relation: { npcId: 'n-nulei', lt: 20 } }, + text: '捉住一个东丘祝氏的家奴,偷我庄上的灵草,被族人绑来听候发落。', + options: [ + { label: '重罚后放回', hint: '祝氏关系-8', eff: { relation: { 'n-nulei': -8 } } }, + { label: '送还祝氏', hint: '祝氏关系+6', eff: { relation: { 'n-nulei': 6 } } }, + { label: '就地正法', hint: '祝氏关系-20,声望+3', eff: { relation: { 'n-nulei': -20 }, rep: 3 } } + ] + }, + { + id: 'ev-lunda', name: '论道会', category: 'daily', weight: 5, + cond: { minAdult: 2 }, + text: '方圆修士开论道会,族人收到请帖,座上皆是气息不凡之辈。', + options: [ + { label: '遣长老赴会', hint: '随机成员修为+15%', eff: { memberBy: { by: 'exp', target: 'highestPower', n: 15 } } }, + { label: '全族观礼', hint: '声望+3', eff: { rep: 3 } }, + { label: '婉拒', hint: '无', eff: {} } + ] + }, + { + id: 'ev-wenyi', name: '西川瘟疫', category: 'major', weight: 7, + cond: { minYear: 2 }, + text: '西川爆发古怪瘟疫,卢氏求药无门,遣人来向庄上求助,愿以族传丹方相谢。', + options: [ + { label: '赠药救人', hint: '灵草-30,灵石-50,木氏关系+15', eff: { res: { lingcao: -30, stones: -50 }, relation: { 'n-danxin': 15 }, rep: 5 } }, + { label: '坐地起价', hint: '灵石+200,木氏关系-15', eff: { res: { stones: 200 }, relation: { 'n-danxin': -15 } } }, + { label: '关门避险', hint: '声望-2', eff: { rep: -2 } } + ] + }, + { + id: 'ev-qiuchee', name: '借剑之请', category: 'major', weight: 5, + cond: { minBuilding: { id: 'cangshu', level: 1 } }, + text: '玄影沈氏遣使来借一部剑典,言按借期十年奉还,愿以两部法器为押。', + options: [ + { label: '借!', hint: '法器+1,沈氏关系+12', eff: { pillGain: { 'weapon-qi': 1 }, relation: { 'n-xuanying': 12 } } }, + { label: '婉拒', hint: '沈氏关系-5', eff: { relation: { 'n-xuanying': -5 } } }, + { label: '借,但要人质', hint: '沈氏关系+2,声望-2', eff: { relation: { 'n-xuanying': 2 }, rep: -2 } } + ] + }, + { + id: 'ev-xiamaozi', name: '神秘夜访', category: 'fate', weight: 4, + once: true, + text: '月黑风高,一位蒙面客深夜造访,自称知晓祖地深处有一处上古洞府,愿以两成收成易开启之诀。', + options: [ + { label: '与他交易', hint: '灵石-120', eff: { res: { stones: -120 }, flag: { caveClue: true }, rep: 2 } }, + { label: '绑了问话', hint: '可能招祸', eff: { flag: { caveRumor: true }, rep: -2 } }, + { label: '赏夜赶出', hint: '无', eff: {} } + ] + }, + { + id: 'ev-zuling', name: '祖祠异动', category: 'fate', weight: 3, + once: true, + cond: { minBuilding: { id: 'zongci', level: 2 } }, + text: '深夜祖祠忽闻钟鸣,牌位前竟现一尊淡影,凝视良久,留下二字:「续裔」。', + options: [ + { label: '上香叩拜', hint: '声望+8,全族修炼+', eff: { rep: 8, memberBy: { by: 'inspire', target: 'all', n: 5 }, flag: { ancestorBless: true } } }, + { label: '祭以三牲', hint: '声望+12', eff: { rep: 12, flag: { ancestorBless: true } } }, + { label: '令人守夜', hint: '无', eff: {} } + ] + }, + { + id: 'ev-leize', name: '万兽潮', category: 'fate', weight: 3, + once: true, + cond: { minYear: 3 }, + text: '数万妖兽从东畔雷泽奔涌而下,漫山遍野,直逼庄墙!须速决断。', + options: [ + { label: '举族死守', hint: '战斗!打赢得兽核与声望', eff: { raid: { npcId: 'n-nulei' }, flag: { beastTide: true } } }, + { label: '迁建宗祠', hint: '声望-6,灵石-100', eff: { res: { stones: -100 }, rep: -6 } }, + { label: '联姻求救', hint: '灵石-80,木氏关系+8', eff: { res: { stones: -80 }, relation: { 'n-danxin': 8 } } } + ] + }, + { + id: 'ev-jobai', name: '卖女求荣', category: 'major', weight: 4, + cond: { minAdult: 1, maxAdult: 8 }, + text: '有媒人上门,说东丘祝氏大公子瞧上族中一位姑娘,愿出三百灵石聘礼换两家结好。', + options: [ + { label: '应下这门亲', hint: '灵石+300,祝氏关系+10', eff: { res: { stones: 300 }, relation: { 'n-nulei': 10 } } }, + { label: '再加价', hint: '灵石+450,祝氏关系-5', eff: { res: { stones: 450 }, relation: { 'n-nulei': -5 } } }, + { label: '回绝', hint: '无', eff: {} } + ] + }, + { + id: 'ev-lingshou', name: '灵兽认主', category: 'major', weight: 4, + cond: { minBuilding: { id: 'lingshou', level: 2 } }, + text: '园中一只幼兽七日不食,气若游丝,唯对一位族人亲昵异常。', + options: [ + { label: '牵线认主', hint: '随机成员获得灵兽伴侣', eff: { memberBy: { by: 'genius', target: 'highestFortune', n: 1 } } }, + { label: '卖了换钱', hint: '灵石+150', eff: { res: { stones: 150 } } }, + { label: '放归山林', hint: '声望+2', eff: { rep: 2 } } + ] + }, + { + id: 'ev-xianyi', name: '残简偶得', category: 'major', weight: 5, + cond: { minBuilding: { id: 'cangshu', level: 1 } }, + text: '有人兜售一页古玉残简,字迹晦暗,却似有大道气息流动。', + options: [ + { label: '买下参悟', hint: '灵石-150', eff: { res: { stones: -150 }, techniqueChance: 0.8 } }, + { label: '献诸官家', hint: '声望+6', eff: { rep: 6 } }, + { label: '不信邪,扔了', hint: '无', eff: {} } + ] + }, + { + id: 'ev-kuangxie', name: '矿脉异动', category: 'major', weight: 4, + cond: { minBuilding: { id: 'lingkuang', level: 2 } }, + text: '矿工急报:井下竟听到龙吟之声,地气翻涌,恐是宝脉将成。', + options: [ + { label: '加派人手', hint: '灵矿+80', eff: { res: { lingkuang: 80 } } }, + { label: '镇压封脉', hint: '灵矿-20,声望+3', eff: { res: { lingkuang: -20 }, rep: 3 } }, + { label: '上报求封赏', hint: '灵石+100,声望+5', eff: { res: { stones: 100 }, rep: 5 } } + ] + }, + { + id: 'ev-yaoqi', name: '玉露灵液', category: 'major', weight: 4, + cond: { minBuilding: { id: 'yaoyuan', level: 1 } }, + text: '药园一株千年石斛今夜吐露玉露,紫光浸雾,实为难得。', + options: [ + { label: '专人采露', hint: '聚气丹+2', eff: { pillGain: { 'pill-qiyuan': 2 } } }, + { label: '留作传家', hint: '声望+5', eff: { rep: 5 } }, + { label: '泡酒宴客', hint: '声望+7', eff: { rep: 7 } } + ] + }, + { + id: 'ev-jiazu', name: '氏族纠纷', category: 'daily', weight: 5, + cond: { minAdult: 3 }, + text: '族中兄弟为一处祖屋的归属争执不下,闹到了宗祠。', + options: [ + { label: '秉公裁断', hint: '声望+3', eff: { rep: 3, memberBy: { by: 'wound', target: 'random', n: 5 } } }, + { label: '各打五十大板', hint: '声望-1', eff: { rep: -1 } }, + { label: '拖到明年再说', hint: '声望-3', eff: { rep: -3 } } + ] + }, + { + id: 'ev-waizushao', name: '修士来投', category: 'major', weight: 5, + cond: { minRep: 20, maxAdult: 12 }, + text: '一位炼气散修慕名而来,愿入庄效力,只是要一份例钱。', + options: [ + { label: '开门收下', hint: '族人+1', eff: { memberBy: { by: 'inspire', target: 'all', n: 0 }, rep: 1, flag: { recruit: true } } }, + { label: '考其心性', hint: '族人+1(或负伤)', eff: { memberBy: { by: 'loot', target: 'highestPower', n: 1 }, flag: { recruit2: true } } }, + { label: '婉拒', hint: '无', eff: {} } + ] + }, + { + id: 'ev-tianlei', name: '天雷淬体', category: 'fate', weight: 2, + once: true, + text: '晴空忽落一道紫雷,劈在宗祠檐角,却汇聚成光球,悬于一位弟子头顶。', + options: [ + { label: '接引练体', hint: '随机成员修为大涨', eff: { memberBy: { by: 'genius', target: 'random', n: 3 } } }, + { label: '存入玉瓶', hint: '聚气丹+3', eff: { pillGain: { 'pill-qiyuan': 3 } } }, + { label: '请道人镇压', hint: '技能+1', eff: { memberBy: { by: 'madness', target: 'random', n: 1 } } } + ] + } +] diff --git a/src/renderer/game/data/items.ts b/src/renderer/game/data/items.ts new file mode 100644 index 0000000..1f2133e --- /dev/null +++ b/src/renderer/game/data/items.ts @@ -0,0 +1,43 @@ +export interface ItemDef { + id: string + name: string + kind: 'resource' | 'pill' | 'artifact' + basePrice: number + desc: string + icon: string + cooldown?: number +} + +export const ITEMS: Record = { + lingcao: { id: 'lingcao', name: '灵草', kind: 'resource', basePrice: 8, desc: '最常见的修炼辅材,亦是炼丹基础。', icon: '草' }, + lingkuang: { id: 'lingkuang', name: '灵矿', kind: 'resource', basePrice: 12, desc: '铸器与布阵常用矿物。', icon: '矿' }, + beastcore: { id: 'beastcore', name: '兽核', kind: 'resource', basePrice: 30, desc: '妖兽一身的精华,可炼丹炼器。', icon: '核' }, + + 'pill-qiyuan': { id: 'pill-qiyuan', name: '聚气丹', kind: 'pill', basePrice: 45, desc: '炼气期修士服之,一月修为大增。', icon: '气' }, + 'pill-ningyuan': { id: 'pill-ningyuan', name: '凝元丹', kind: 'pill', basePrice: 150, desc: '筑基以上可效,修为增长显著。', icon: '元' }, + 'pill-pojing': { id: 'pill-pojing', name: '破境丹', kind: 'pill', basePrice: 480, desc: '冲击瓶颈时的辅助神物,提升突破成功率。', icon: '破' }, + + 'weapon-fan': { id: 'weapon-fan', name: '凡器', kind: 'artifact', basePrice: 120, desc: '普通铁器,聊胜于无。', icon: '凡' }, + 'weapon-qi': { id: 'weapon-qi', name: '法器', kind: 'artifact', basePrice: 450, desc: '蕴灵之器,可引动灵力。', icon: '法' }, + 'weapon-ling': { id: 'weapon-ling', name: '灵器', kind: 'artifact', basePrice: 1400, desc: '有灵之器,锋芒隐露。', icon: '灵' }, + 'weapon-fa': { id: 'weapon-fa', name: '法宝', kind: 'artifact', basePrice: 3600, desc: '罕世法宝,非金丹不能驾驭。', icon: '宝' } +} + +export const ARTIFACT_POWER: Record = { + 'weapon-fan': 0.1, + 'weapon-qi': 0.25, + 'weapon-ling': 0.5, + 'weapon-fa': 0.9 +} + +export function itemById(id: string): ItemDef { + return ITEMS[id] +} + +export interface SimpleTradeItem { + id: string + name: string + price: number + desc: string + count: number +} diff --git a/src/renderer/game/data/npcs.ts b/src/renderer/game/data/npcs.ts new file mode 100644 index 0000000..f22da2d --- /dev/null +++ b/src/renderer/game/data/npcs.ts @@ -0,0 +1,43 @@ +import { RealmMajor } from '../types/domain' + +export interface NpcFamilyDef { + id: string + name: string + region: string + desc: string + style: string + leaderRealm: RealmMajor + initialPower: number + powerGrowth: [number, number] + sells?: string[] + buys?: string[] +} + +export const NPCS: NpcFamilyDef[] = [ + { + id: 'n-xuanying', name: '玄影沈氏', region: '北岳玄影峰', style: '剑修世家', desc: '隐于北岳的剑修吕氏,剑意凛冽,最为孤傲。', + leaderRealm: 'foundation', initialPower: 240, powerGrowth: [4, 10], + sells: ['weapon-qi', 'weapon-ling'], buys: ['lingcao', 'lingkuang'] + }, + { + id: 'n-danxin', name: '丹心木氏', region: '西川药谷', style: '丹道世家', desc: '悬壶济世的丹道世家,人脉广博,风格和缓。', + leaderRealm: 'foundation', initialPower: 200, powerGrowth: [3, 9], + sells: ['pill-qiyuan', 'pill-ningyuan', 'pill-pojing'], buys: ['lingcao', 'beastcore'] + }, + { + id: 'n-sihai', name: '四海王氏', region: '南都连港', style: '商盟世族', desc: '商通四海,富可敌国,只认灵石不认人。', + leaderRealm: 'foundation', initialPower: 160, powerGrowth: [5, 12], + sells: ['lingcao', 'lingkuang'], buys: ['beastcore', 'lingkuang'] + }, + { + id: 'n-nulei', name: '怒雷祝氏', region: '东丘雷泽', style: '兵修蛮门', desc: '雷泽蛮族的世仇,性情火爆,最易生衅。', + leaderRealm: 'core', initialPower: 300, powerGrowth: [5, 13], + sells: [], buys: ['lingkuang', 'beastcore'] + } +] + +export function npcById(id: string): NpcFamilyDef { + const n = NPCS.find((x) => x.id === id) + if (!n) throw new Error(`npc not found: ${id}`) + return n +} diff --git a/src/renderer/game/data/pacing.ts b/src/renderer/game/data/pacing.ts new file mode 100644 index 0000000..cd93b16 --- /dev/null +++ b/src/renderer/game/data/pacing.ts @@ -0,0 +1,14 @@ +import { RealmMajor } from '../types/domain' + +export const MAJOR_RATE: Record = { + mortal: 1, + qi: 1, + foundation: 0.62, + core: 0.38, + nascent: 0.23, + spirit: 0.15 +} + +export function masteryRateOfMajor(major: RealmMajor): number { + return MAJOR_RATE[major] ?? 1 +} diff --git a/src/renderer/game/data/realms.ts b/src/renderer/game/data/realms.ts new file mode 100644 index 0000000..facd9ea --- /dev/null +++ b/src/renderer/game/data/realms.ts @@ -0,0 +1,119 @@ +import { Element, Realm, RealmMajor } from '../types/domain' + +export interface MajorDef { + name: string + short: string + minorLayers: number + lifespan: number + // 修为点数 第1层到满层所需(每层指数) + expBase: number + expGrowth: number +} + +export const MAJORS: Record = { + mortal: { name: '凡人', short: '凡', minorLayers: 0, lifespan: 62, expBase: 0, expGrowth: 1 }, + qi: { name: '炼气', short: '炼气', minorLayers: 9, lifespan: 110, expBase: 60, expGrowth: 1.35 }, + foundation: { name: '筑基', short: '筑基', minorLayers: 3, lifespan: 165, expBase: 1400, expGrowth: 1.6 }, + core: { name: '金丹', short: '金丹', minorLayers: 3, lifespan: 260, expBase: 6200, expGrowth: 1.7 }, + nascent: { name: '元婴', short: '元婴', minorLayers: 3, lifespan: 400, expBase: 22000, expGrowth: 1.75 }, + spirit: { name: '化神', short: '化神', minorLayers: 3, lifespan: 600, expBase: 60000, expGrowth: 1.8 } +} + +export const MAJOR_ORDER: RealmMajor[] = ['mortal', 'qi', 'foundation', 'core', 'nascent', 'spirit'] + +export const MAJOR_NAMES: Record = { + mortal: '凡人', + qi: '炼气', + foundation: '筑基', + core: '金丹', + nascent: '元婴', + spirit: '化神' +} + +export function describeRealm(realm: Realm): string { + if (realm.major === 'mortal') return '凡人' + const def = MAJORS[realm.major] + return `${def.name}${realm.minor + 1}层` +} + +export function realmTier(realm: Realm): number { + return MAJOR_ORDER.indexOf(realm.major) * 10 + (realm.major === 'mortal' ? 0 : realm.minor + 1) +} + +export function compareRealm(a: Realm, b: Realm): number { + return realmTier(a) - realmTier(b) +} + +export function maxRealmExp(major: RealmMajor, minor: number): number { + if (major === 'mortal') return 0 + const def = MAJORS[major] + return Math.round(def.expBase * Math.pow(def.expGrowth, minor)) +} + +export function nextRealm(realm: Realm): Realm | null { + const idx = MAJOR_ORDER.indexOf(realm.major) + if (realm.major === 'mortal') return { major: 'qi', minor: 0 } + if (idx >= MAJOR_ORDER.length - 1 && realm.minor >= MAJORS[realm.major as RealmMajor].minorLayers - 1) return null + const def = MAJORS[realm.major as RealmMajor] + if (realm.minor + 1 < def.minorLayers) return { major: realm.major, minor: realm.minor + 1 } + return { major: MAJOR_ORDER[idx + 1], minor: 0 } +} + +export function basePower(realm: Realm): number { + if (realm.major === 'mortal') return 1 * (realm.minor * 0.15 + 1) + const def = MAJORS[realm.major] + return Math.pow(MODEL_BASE, MAJOR_ORDER.indexOf(realm.major)) * (1 + realm.minor * 0.18) +} + +const MODEL_BASE = 3.4 + +export function realmDeathChance(realm: Realm, mind: number): number { + switch (realm.major) { + case 'mortal': + return 0.002 + case 'qi': + return 0.004 + case 'foundation': + return 0.008 + case 'core': + return 0.02 + case 'nascent': + return 0.035 + case 'spirit': + return 0.05 + default: + return 0.005 + } +} + +export function breakthroughBaseChance(realm: Realm): number { + switch (realm.major) { + case 'mortal': + return 0.9 + case 'qi': + return 0.45 + case 'foundation': + return 0.32 + case 'core': + return 0.22 + case 'nascent': + return 0.14 + case 'spirit': + return 0.08 + default: + return 0.4 + } +} + +export interface TechniqueDef { + id: string + name: string + grade: number + element: Element + path: string + expBonus: number + powerBonus: number + desc: string +} + +export const TECHNIQUE_GRADE_NAMES = ['黄阶', '玄阶', '地阶', '天阶', '仙阶'] diff --git a/src/renderer/game/data/secrets.ts b/src/renderer/game/data/secrets.ts new file mode 100644 index 0000000..3ab0c40 --- /dev/null +++ b/src/renderer/game/data/secrets.ts @@ -0,0 +1,117 @@ +import { RealmMajor } from '../types/domain' + +export interface EnemyDef { + id: string + name: string + realm: RealmMajor + strength: number + icon: string + desc: string +} + +export const ENEMIES: EnemyDef[] = [ + { id: 'e-huiyuan', name: '灰狼王', realm: 'qi', strength: 0.8, icon: '狼', desc: '啸聚山林的妖兽头领,凶性难驯。' }, + { id: 'e-tiebei', name: '铁背熊罴', realm: 'qi', strength: 1.1, icon: '熊', desc: '皮糙肉厚的山中猛兽,以力御敌。' }, + { id: 'e-muche', name: '赤瞳木魅', realm: 'foundation', strength: 1.0, icon: '木', desc: '千年老树成精,嗜血成性。' }, + { id: 'e-baiqi', name: '白骨修士', realm: 'foundation', strength: 1.2, icon: '骨', desc: '枯骨道人的傀儡余孽,邪气森森。' }, + { id: 'e-huanhan', name: '幻寒螭', realm: 'core', strength: 1.15, icon: '螭', desc: '远古寒螭之遗种,吐息生霜。' }, + { id: 'e-yeshen', name: '夜哭古影', realm: 'core', strength: 1.3, icon: '影', desc: '怨念凝结,闻见其声者神魂惑乱。' }, + { id: 'e-jiaoyun', name: '焦云妖鹤', realm: 'nascent', strength: 1.2, icon: '鹤', desc: '焚山煮海的老妖,万兽避让。' }, + { id: 'e-kuangzun', name: '堕墟魔尊', realm: 'spirit', strength: 1.5, icon: '魔', desc: '屠戮一族的魔道巨擘,举手投足皆天威。' } +] + +export interface MissionStageDef { + kind: 'event' | 'combat' | 'resource' | 'boss' + months: number + title: string + text?: { safe?: string; good?: string; bad?: string } + enemyId?: string + loot?: LootDef +} + +export interface LootDef { + resources: Record + artifactChance: number + techniqueChance: number + beastcoreChance?: number +} + +export interface MissionDef { + id: string + name: string + region: string + icon: string + desc: string + realmHint: RealmMajor + minMembers: number + maxMembers: number + risk: number + stages: MissionStageDef[] + completionLoot: LootDef +} + +export const MISSIONS: MissionDef[] = [ + { + id: 'm-anmoku', name: '暗墨林', region: '祖城西郊', icon: '林', + desc: '灵气暗涌的密林,常有低阶妖兽出没,是子弟试炼的好去处。', + realmHint: 'qi', minMembers: 1, maxMembers: 4, risk: 0.5, + stages: [ + { kind: 'event', months: 3, title: '深入林间', text: { safe: '一路无波,采得几株灵草压惊。', bad: '林深迷路,误入瘴气,队伍折损了体力。' } }, + { kind: 'combat', months: 2, title: '遭遇袭击', enemyId: 'e-tiebei' }, + { kind: 'resource', months: 2, title: '寻获残洞', loot: { resources: { lingcao: [30, 80], beastcore: [1, 3] }, artifactChance: 0.05, techniqueChance: 0.03 } } + ], + completionLoot: { resources: { lingcao: [20, 60], lingkuang: [5, 15] }, artifactChance: 0.08, techniqueChance: 0.03 } + }, + { + id: 'm-xuangu', name: '玄冰谷', region: '北境寒渊', icon: '冰', + desc: '寒潭彻骨,谷中灵泉可淬骨洗髓,也匿着护泉的凶兽。', + realmHint: 'qi', minMembers: 1, maxMembers: 4, risk: 0.65, + stages: [ + { kind: 'resource', months: 3, title: '踏雪寻泉', loot: { resources: { lingkuang: [20, 50] }, artifactChance: 0.02, techniqueChance: 0.02 } }, + { kind: 'combat', months: 2, title: '泉主现身', enemyId: 'e-huiyuan' }, + { kind: 'boss', months: 2, title: '冰潭之下', enemyId: 'e-baiqi' } + ], + completionLoot: { resources: { lingkuang: [30, 70], beastcore: [2, 5] }, artifactChance: 0.15, techniqueChance: 0.06 } + }, + { + id: 'm-guzhan', name: '古战阵', region: '南陵荒原', icon: '战', + desc: '上古两族交兵的战场,杀气千年未散,宝物与凶险兼存。', + realmHint: 'foundation', minMembers: 2, maxMembers: 4, risk: 0.75, + stages: [ + { kind: 'event', months: 3, title: '踏勘阵眼', text: { safe: '寻得阵眼遗骸,得一部残简。', bad: '触动禁制,一行人负伤遁走。' } }, + { kind: 'combat', months: 2, title: '怨灵缠身', enemyId: 'e-baiqi' }, + { kind: 'combat', months: 2, title: '守阵铁骑', enemyId: 'e-huanhan' }, + { kind: 'resource', months: 3, title: '挖开枯井', loot: { resources: { lingkuang: [40, 90], beastcore: [2, 6] }, artifactChance: 0.2, techniqueChance: 0.08 } } + ], + completionLoot: { resources: { lingkuang: [40, 90], beastcore: [2, 8] }, artifactChance: 0.22, techniqueChance: 0.1 } + }, + { + id: 'm-lingshan', name: '灵鹫山', region: '东海之滨', icon: '鹫', + desc: '孤峰入云,鹫鸣震谷,山腹有灵矿与丹谷仙人遗泽。', + realmHint: 'foundation', minMembers: 2, maxMembers: 4, risk: 0.85, + stages: [ + { kind: 'combat', months: 2, title: '登山遇鹫', enemyId: 'e-muche' }, + { kind: 'resource', months: 3, title: '灵鹫巢穴', loot: { resources: { lingkuang: [50, 120], beastcore: [3, 8] }, artifactChance: 0.25, techniqueChance: 0.15 } }, + { kind: 'boss', months: 3, title: '丹谷试剑', enemyId: 'e-yeshen' } + ], + completionLoot: { resources: { lingcao: [60, 140], beastcore: [4, 10] }, artifactChance: 0.3, techniqueChance: 0.2 } + }, + { + id: 'm-tiankeng', name: '天地秘境', region: '传说天外', icon: '玄', + desc: '百年一开的无上秘境,传言其中有登天造化。', + realmHint: 'core', minMembers: 3, maxMembers: 5, risk: 1.0, + stages: [ + { kind: 'event', months: 2, title: '踏入秘境', text: { safe: '奇花异果满目,造化之气扑面。', bad: '紫霄神雷击碎阵法,众人惊魂不定。' } }, + { kind: 'combat', months: 2, title: '守宫神兽', enemyId: 'e-huanhan' }, + { kind: 'combat', months: 3, title: '天劫傀儡', enemyId: 'e-jiaoyun' }, + { kind: 'boss', months: 3, title: '群雄夺宝', enemyId: 'e-kuangzun' } + ], + completionLoot: { resources: { lingcao: [100, 220], lingkuang: [80, 160], beastcore: [10, 22] }, artifactChance: 0.6, techniqueChance: 0.5 } + } +] + +export function missionById(id: string): MissionDef { + const m = MISSIONS.find((x) => x.id === id) + if (!m) throw new Error(`mission not found: ${id}`) + return m +} diff --git a/src/renderer/game/data/techniques.ts b/src/renderer/game/data/techniques.ts new file mode 100644 index 0000000..212fad2 --- /dev/null +++ b/src/renderer/game/data/techniques.ts @@ -0,0 +1,27 @@ +import { TechniqueDef } from './realms' + +export const TECHNIQUES: TechniqueDef[] = [ + { id: 't-qinglian', name: '青莲剑诀', grade: 1, element: '木', path: '剑修', expBonus: 0.12, powerBonus: 0.15, desc: '青莲花开,剑气如雨。' }, + { id: 't-canglei', name: '苍雷炼体功', grade: 1, element: '金', path: '体修', expBonus: 0.1, powerBonus: 0.2, desc: '引雷淬体,肉身如兵。' }, + { id: 't-xuanyue', name: '玄月斩灵诀', grade: 1, element: '水', path: '剑修', expBonus: 0.12, powerBonus: 0.1, desc: '月华落处,万灵俯首。' }, + { id: 't-lieliuxin', name: '离火心经', grade: 1, element: '火', path: '丹修', expBonus: 0.14, powerBonus: 0.08, desc: '以火养心,丹道通神。' }, + { id: 't-houtu', name: '厚土镇岳功', grade: 1, element: '土', path: '体修', expBonus: 0.1, powerBonus: 0.16, desc: '不动如山,守御无双。' }, + { id: 't-baihui', name: '百草经', grade: 1, element: '木', path: '丹修', expBonus: 0.13, powerBonus: 0.06, desc: '识百草而济世人。' }, + { id: 't-zhenyu', name: '阵纹天书', grade: 2, element: '土', path: '阵修', expBonus: 0.16, powerBonus: 0.12, desc: '经纬天地,伏杀千里。' }, + { id: 't-hanyu', name: '寒玉心法', grade: 2, element: '水', path: '剑修', expBonus: 0.15, powerBonus: 0.18, desc: '心如寒玉,剑出无痕。' }, + { id: 't-liehuo', name: '烈火焚空诀', grade: 2, element: '火', path: '剑修', expBonus: 0.16, powerBonus: 0.24, desc: '烈焰焚空,寸草不留。' }, + { id: 't-wanjian', name: '万剑归一诀', grade: 2, element: '金', path: '剑修', expBonus: 0.14, powerBonus: 0.28, desc: '万道剑影,尽归一念。' }, + { id: 't-yushen', name: '驭兽真解', grade: 2, element: '木', path: '御灵', expBonus: 0.15, powerBonus: 0.14, desc: '万物有灵,皆可为兵。' }, + { id: 't-geling', name: '化灵经', grade: 3, element: '水', path: '符修', expBonus: 0.2, powerBonus: 0.22, desc: '墨落成灵,一符镇幽。' }, + { id: 't-jiuxiao', name: '九霄龙吟诀', grade: 3, element: '金', path: '剑修', expBonus: 0.18, powerBonus: 0.35, desc: '龙吟九霄,一剑开天。' }, + { id: 't-tianya', name: '天涯孤鸿法', grade: 3, element: '火', path: '符修', expBonus: 0.21, powerBonus: 0.25, desc: '孤鸿照影,天涯为路。' }, + { id: 't-qiankun', name: '乾坤七星阵', grade: 4, element: '土', path: '阵修', expBonus: 0.25, powerBonus: 0.3, desc: '七星成阵,乾坤为牢。' }, + { id: 't-xiantian', name: '先天混元功', grade: 4, element: '木', path: '体修', expBonus: 0.28, powerBonus: 0.38, desc: '混元一气,万法不侵。' } +] + +export function techniqueById(id: string | undefined): TechniqueDef | null { + if (!id) return null + return TECHNIQUES.find((t) => t.id === id) ?? null +} + +export const TECHNIQUES_MARKET_POOL = TECHNIQUES.filter((t) => t.grade <= 3) diff --git a/src/renderer/game/data/traits.ts b/src/renderer/game/data/traits.ts new file mode 100644 index 0000000..2fde00a --- /dev/null +++ b/src/renderer/game/data/traits.ts @@ -0,0 +1,31 @@ +export interface TraitDef { + id: string + name: string + desc: string + expBonus?: number + breakBonus?: number + windBonus?: number + charmBonus?: number + danger: number + priceMult?: number +} + +export const TRAITS: Record = { + tiangan: { id: 'tiangan', name: '坚毅', desc: '突破成功率提升,突破失败代价减轻。', breakBonus: 0.06, expBonus: 0, danger: 0 }, + jizao: { id: 'jizao', name: '急躁', desc: '突破更鲁莽,失败代价更高,但修炼略快。', expBonus: 0.08, danger: 0.7 }, + shensui: { id: 'shensui', name: '沉静', desc: '心性稳重,突破不轻易失败。', breakBonus: 0.05, danger: 0 }, + xinheng: { id: 'xinheng', name: '性狠', desc: '战时战力提升,掳掠资源更多。', windBonus: 0.06, danger: 0.2 }, + haoxiao: { id: 'haoxiao', name: '嗜酒', desc: '心境易起伏,但与人亲近。', charmBonus: 0.04, danger: 0.15 }, + xinnian: { id: 'xinnian', name: '多情', desc: '魅力提升,联姻时好感度更高。', charmBonus: 0.08, danger: 0.1 }, + zisheng: { id: 'zisheng', name: '自律', desc: '修为获取速度提升。', expBonus: 0.1, danger: 0 }, + lijian: { id: 'lijian', name: '利己', desc: '贸易买卖价格更优。', priceMult: 0.05, danger: 0.3 }, + keji: { id: 'keji', name: '克己', desc: '重伤时不易陨落。', breakBonus: 0.02, danger: 0 }, + youmo: { id: 'youmo', name: '潇洒', desc: '风波中更易化险为夷。', windBonus: 0.05, danger: 0 }, + guguai: { id: 'guguai', name: '古怪', desc: '行为难以捉摸,战力微增。', windBonus: 0.08, danger: 0.3 }, + chiyi: { id: 'chiyi', name: '斥医', desc: '不喜丹药,突破失败易重伤。', breakBonus: -0.04, danger: 0.6 }, + jingkan: { id: 'jingkan', name: '精研', desc: '闭关修炼效率更高。', expBonus: 0.12, danger: 0 }, + haoyi: { id: 'haoyi', name: '豪义', desc: '声望获取更多。', charmBonus: 0.06, danger: 0.2 }, + yiqi: { id: 'yiqi', name: '易喜', desc: '脾气如火,战斗极勇。', windBonus: 0.1, danger: 0.5 } +} + +export const TRAIT_POOL = Object.keys(TRAITS) diff --git a/src/renderer/game/engine/creation.ts b/src/renderer/game/engine/creation.ts new file mode 100644 index 0000000..19675d8 --- /dev/null +++ b/src/renderer/game/engine/creation.ts @@ -0,0 +1,192 @@ +import { Character, GameState, NpcFamilyState, RealmMajor } from '../types/domain' +import { Rng, seedToRng } from '../core/rng' +import { randomSurname, MALE_GIVEN, FEMALE_GIVEN } from '../core/names' +import { newCharacter } from './pcgen' +import { NPCS } from '../data/npcs' +import { World } from './world' + +export interface NewGameOptions { + seed: string + surname: string + familyName: string + motto: string + difficulty: 'easy' | 'normal' | 'hard' +} + +export function createWorldState(opts: NewGameOptions): GameState { + const rng = new Rng(seedToRng(opts.seed)) + const surname = opts.surname.trim() || randomSurname(rng) + const familyName = opts.familyName.trim() || `${surname}家` + const diff = opts.difficulty + const stones = diff === 'easy' ? 1200 : diff === 'normal' ? 800 : 550 + const npcStrength = diff === 'easy' ? 0.9 : diff === 'normal' ? 1 : 1.15 + + const state: GameState = { + schemaVersion: 1, + seed: opts.seed, + rng: rng.getState(), + year: 1, + month: 1, + seq: 0, + family: { + surname, + name: familyName, + motto: opts.motto.trim() || '耕读传家,术法继世', + crest: '#c9a227', + estate: '青云庄', + yearFounded: 1, + generation: 1, + reputation: 5, + stones, + inventory: { + lingcao: 60, + lingkuang: 30, + beastcore: 0, + 'pill-qiyuan': 2, + 'pill-ningyuan': 1 + }, + buildings: { lingtian: 1, zongci: 1 }, + techniques: ['t-qinglian', 't-houtu'], + missionIds: [], + headId: '', + difficulty: diff, + flag: { priceMult: 1, tenants: 0, headBless: 0 } + }, + members: {}, + npcFamilies: Object.fromEntries( + NPCS.map((n) => [ + n.id, + { + id: n.id, + name: n.name, + region: n.region, + power: Math.round(n.initialPower * npcStrength), + relation: 0, + allied: false, + raidCount: 0 + } as NpcFamilyState + ]) + ), + missions: [], + chronicle: [], + battles: [], + eventQueue: [], + completedEvents: [], + flags: {}, + totalTicks: 0 + } + + const w = new World(state, []) + const male1: string = rng.pick(MALE_GIVEN) + const female1: string = rng.pick(FEMALE_GIVEN) + + const head: Character = newCharacter(rng, { + name: `${surname}${male1}`, + gender: 'male', + generation: 1, + bornYear: 1 - 35, + age: 35, + realm: { major: 'qi', minor: 4 }, + isHead: true, + isFounder: true, + fortuneBase: 7 + }) + head.realmProgress = 40 + head.techniqueId = 't-qinglian' + head.traits = ['tiangan', 'shensui'] + + const wife: Character = newCharacter(rng, { + name: `${surname}${female1}`, + gender: 'female', + generation: 1, + bornYear: 1 - 33, + age: 33, + realm: { major: 'qi', minor: 2 }, + fortuneBase: 6 + }) + wife.realmProgress = 55 + head.spouseId = 'x2' + wife.spouseId = 'x1' + head.children = ['x3', 'x5'] + + const elderBrother: Character = newCharacter(rng, { + name: `${surname}${rng.pick(MALE_GIVEN)}`, + gender: 'male', + generation: 2, + bornYear: 1 - 16, + age: 16, + realm: { major: 'qi', minor: 1 }, + father: head, + mother: wife + }) + elderBrother.realmProgress = 20 + elderBrother.techniqueId = 't-houtu' + elderBrother.fatherId = 'x1' + elderBrother.motherId = 'x2' + + const sister: Character = newCharacter(rng, { + name: `${surname}${rng.pick(FEMALE_GIVEN)}`, + gender: 'female', + generation: 2, + bornYear: 1 - 12, + age: 12, + realm: { major: 'mortal', minor: 0 }, + father: head, + mother: wife + }) + sister.fatherId = 'x1' + sister.motherId = 'x2' + + const uncle: Character = newCharacter(rng, { + name: `${surname}${rng.pick(MALE_GIVEN)}`, + gender: 'male', + generation: 1, + bornYear: 1 - 45, + age: 45, + realm: { major: 'qi', minor: 6 }, + fortuneBase: 6 + }) + uncle.realmProgress = 30 + uncle.techniqueId = 't-houtu' + uncle.traits = ['xinheng', 'shensui'] + uncle.id = 'x4' + uncle.spouseHouse = 'sihai王氏' + uncle.children = [] + + head.id = 'x1' + wife.id = 'x2' + elderBrother.id = 'x3' + sister.id = 'x5' + wife.children = ['x3', 'x5'] + + state.members = { x1: head, x2: wife, x3: elderBrother, x4: uncle, x5: sister } + state.family.headId = 'x1' + state.seq = 10 + + w.chronicle('misc', `${surname}氏一族定居山阴,立${familyName}。庄主${head.name},年方三十五。`, head.id, true) + w.log('info', `青云庄立,${familyName}始兴。`) + return state +} + +export function findInheritor(world: World): Character | undefined { + const alive = world.aliveMembers() + if (alive.length === 0) return undefined + const head = world.state.members[world.state.family.headId] + const candidates = alive.filter((c) => c.id !== head?.id) + if (candidates.length === 0) return undefined + const byBlood = candidates + .filter((c) => (head && head.children.includes(c.id)) || (c.fatherId === head?.id)) + .sort((a, b) => world.ageOf(b) - world.ageOf(a)) + if (byBlood.length > 0) return byBlood[0] + const byRealm = [...candidates].sort((a, b) => { + const ra = realmRank(a.realm) + const rb = realmRank(b.realm) + return rb - ra || b.charm - a.charm || world.ageOf(b) - world.ageOf(a) + }) + return byRealm[0] +} + +function realmRank(realm: { major: RealmMajor; minor: number }): number { + const order: RealmMajor[] = ['mortal', 'qi', 'foundation', 'core', 'nascent', 'spirit'] + return order.indexOf(realm.major) * 10 + realm.minor +} diff --git a/src/renderer/game/engine/market.ts b/src/renderer/game/engine/market.ts new file mode 100644 index 0000000..e28d4d0 --- /dev/null +++ b/src/renderer/game/engine/market.ts @@ -0,0 +1,48 @@ +import { World } from './world' +import { ITEMS } from '../data/items' + +export function marketPrice(w: World, itemId: string): number { + const base = ITEMS[itemId]?.basePrice ?? 1 + const fam = w.state.family + const mult = typeof fam.flag['priceMult'] === 'number' ? (fam.flag['priceMult'] as number) : 1 + const mood = fam.reputation >= 40 ? 1.06 : fam.reputation >= 20 ? 1.02 : 0.98 + return Math.max(1, Math.round(base * mult * mood)) +} + +export function buyItem(w: World, itemId: string, count: number): boolean { + const fam = w.state.family + const total = marketPrice(w, itemId) * count + if (total > fam.stones) return false + fam.stones -= total + fam.inventory[itemId] = (fam.inventory[itemId] ?? 0) + count + return true +} + +export function sellItem(w: World, itemId: string, count: number): boolean { + const fam = w.state.family + const have = fam.inventory[itemId] ?? 0 + if (have < count) return false + fam.inventory[itemId] = have - count + fam.stones += marketPrice(w, itemId) * count + return true +} + +export function buyTechnique(w: World, techId: string, price: number): boolean { + const fam = w.state.family + if (fam.techniques.includes(techId)) return false + if (fam.stones < price) return false + fam.stones -= price + fam.techniques.push(techId) + return true +} + +export function techniquePrice(techId: string): number { + const grade = TECH_GRADE_BASE[techId] ?? 200 + return grade +} + +import { TECHNIQUES } from '../data/techniques' + +const TECH_GRADE_BASE: Record = Object.fromEntries( + TECHNIQUES.map((t) => [t.id, [120, 300, 700, 1600, 3600][t.grade] ?? 300]) +) diff --git a/src/renderer/game/engine/pcgen.ts b/src/renderer/game/engine/pcgen.ts new file mode 100644 index 0000000..a50b3d9 --- /dev/null +++ b/src/renderer/game/engine/pcgen.ts @@ -0,0 +1,120 @@ +import { Character, Element, Gender, Realm, RealmMajor } from '../types/domain' +import { Rng } from '../core/rng' +import { ELEMENT_LIST, ROOT_GRADES } from '../data/elements' +import { MAJORS } from '../data/realms' +import { TRAIT_POOL, TRAITS } from '../data/traits' + +export function rollRoots(rng: Rng, parents?: { m?: Character; f?: Character }): { grade: number; primary: Element; secondary: Element[] } { + let grade: number + if (parents && parents.m && parents.f) { + const mix = (parents.m.roots.grade + parents.f.roots.grade) / 2 + const roll = rng.next() + if (roll < 0.3) grade = Math.round(mix) + else if (roll < 0.8) grade = Math.round(mix) + 1 + else grade = Math.round(mix) - 1 + if (rng.chance(0.15)) grade = Math.min(5, grade + 2) + } else { + const total = Object.entries(ROOT_GRADES).reduce((s, [k, v]) => s + v.drawWeight, 0) + let r = rng.next() * total + grade = 1 + for (const [k, v] of Object.entries(ROOT_GRADES)) { + r -= v.drawWeight + if (r <= 0) { + grade = Number(k) + break + } + } + } + grade = Math.max(0, Math.min(5, grade)) + + const primaryCandidate = parents && (parents.m || parents.f) + ? [parents.m!.roots.primary, parents.f!.roots.primary] + : ELEMENT_LIST + const primary = rng.pick(primaryCandidate) + const secondaryCount = grade >= 3 ? rng.int(1, 2) : grade === 2 ? rng.int(0, 1) : 0 + const rest = ELEMENT_LIST.filter((e) => e !== primary) + const secondary = rng.shuffle(rest).slice(0, secondaryCount) + return { grade, primary, secondary } +} + +export function rollPersonality(rng: Rng): string[] { + const n = rng.chance(0.5) ? 2 : 1 + return rng.shuffle([...TRAIT_POOL]).slice(0, n) +} + +export function rollAttributes(rng: Rng, base: number, variance: number): number { + const v = Math.round(base + rng.between(-variance, variance)) + return Math.max(1, Math.min(10, v)) +} + +export function calcLifespan(major: RealmMajor, physique: number): number { + const base = MAJORS[major].lifespan + return Math.round(base * (0.9 + physique * 0.02)) +} + +export function newCharacter( + rng: Rng, + opts: { + name: string + gender: Gender + generation: number + bornYear: number + age: number + mother?: Character + father?: Character + realm?: Realm + isHead?: boolean + isFounder?: boolean + fortuneBase?: number + } +): Character { + const realm: Realm = opts.realm ?? { major: 'mortal', minor: 0 } + const roots = rollRoots(rng, opts.father || opts.mother ? { m: opts.father, f: opts.mother } : undefined) + const levelBonus = MAJORS[realm.major as RealmMajor].minorLayers > 0 ? realm.minor * 0.4 : 0 + const perception = rollAttributes(rng, 4.5 + roots.grade * 0.8 + levelBonus * 0.25, 1.8) + const physique = rollAttributes(rng, 4 + roots.grade * 0.5 + levelBonus * 0.3, 1.8) + const mind = rollAttributes(rng, 4.5 + roots.grade * 0.3, 1.8) + const charm = rollAttributes(rng, 4.5, 2.2) + const fortune = rollAttributes(rng, (opts.fortuneBase ?? 5) + roots.grade * 0.4, 2.2) + return { + id: '', + name: opts.name, + gender: opts.gender, + generation: opts.generation, + children: [], + bornYear: opts.bornYear, + age: opts.age, + realm, + realmProgress: 0, + roots, + perception, + physique, + mind, + charm, + fortune, + traits: rollPersonality(rng), + state: 'idle', + health: 100, + alive: true, + isHead: opts.isHead, + isFounder: opts.isFounder + } +} + +export function traitBonuses(character: Character): { exp: number; breakBonus: number; windBonus: number; charmBonus: number; priceMult: number } { + let exp = 0 + let breakBonus = 0 + let windBonus = 0 + let charmBonus = 0 + let priceMult = 0 + for (const t of character.traits) { + const def = TRAITS[t] + if (!def) continue + exp += def.expBonus ?? 0 + breakBonus += def.breakBonus ?? 0 + windBonus += def.windBonus ?? 0 + charmBonus += def.charmBonus ?? 0 + priceMult += def.priceMult ?? 0 + } + return { exp: 1 + exp, breakBonus, windBonus, charmBonus, priceMult } +} diff --git a/src/renderer/game/engine/systems/combat.ts b/src/renderer/game/engine/systems/combat.ts new file mode 100644 index 0000000..23e5636 --- /dev/null +++ b/src/renderer/game/engine/systems/combat.ts @@ -0,0 +1,213 @@ +import { World } from '../world' +import { Character, BattleLog, NpcFamilyState } from '../../types/domain' +import { basePower, describeRealm } from '../../data/realms' +import { ARTIFACT_POWER } from '../../data/items' +import { techniqueById } from '../../data/techniques' +import { EnemyDef, LootDef } from '../../data/secrets' +import { TECHNIQUES } from '../../data/techniques' +import { traitBonuses } from '../pcgen' +import { npcById } from '../../data/npcs' + +export function combatPowerOf(w: World, c: Character): number { + if (!c.alive) return 0 + const base = basePower(c.realm) + const stat = 1 + (c.perception + c.physique) / 32 + const tech = techniqueById(c.techniqueId) + const techBonus = tech ? 1 + tech.powerBonus : 1 + const equip = c.equipment ? 1 + (ARTIFACT_POWER[c.equipment] ?? 0) : 1 + const trait = 1 + traitBonuses(c).windBonus + const health = 0.5 + 0.5 * (c.health / 100) + return round1(base * stat * techBonus * equip * trait * health) +} + +function round1(n: number): number { + return Math.round(n * 10) / 10 +} + +export function enemyPowerOf(enemy: EnemyDef, risk: number): number { + const base = basePower({ major: enemy.realm, minor: 2 }) + return Math.round(base * enemy.strength * (1.05 + risk * 0.55)) +} + +export function npcPowerOf(npc: NpcFamilyState): number { + return Math.round(npc.power) +} + +export interface EncounterResult { + win: boolean + draw: boolean + lines: string[] + loot?: Record + losses: string[] +} + +const WIN_DESC = [ + '你我咬紧牙关,剑光铺天盖地,那厮节节败退。', + '阵中爆出一声大喝,众人齐攻要害,对方哀嚎退走。', + '硬撼三合,杀得对方胆寒,丢下敌辎拽着尾巴逃了。' +] +const LOSE_DESC = [ + '对方攻势如潮,我方左支右绌,且战且退。', + '眼睁睁瞧着族中子弟咳血倒地,只得弃了阵脚。', + '护山大阵差点被轰裂,残兵败将忍着羞辱撤回。', + '突袭来得隐秘,伤亡不小,幸好退路还在。' +] +const DRAW_DESC = [ + '杀了个天昏地暗,双方均伤,各自罢手。', + '僵持半晌,天入暮色,双方收阵戒备而退。' +] + +export function resolveEncounter( + w: World, + opts: { + title: string + enemy: EnemyDef + risk: number + team: Character[] + kind: BattleLog['kind'] + year: number + month: number + } +): EncounterResult { + let team = 0 + for (const c of opts.team) team += combatPowerOf(w, c) + const enemy = enemyPowerOf(opts.enemy, opts.risk) + const jitter = w.rng.between(0.88, 1.12) + const teamFinal = Math.round(team * jitter) + const roll = w.rng.next() + const win = teamFinal >= enemy * 1.08 + const lose = teamFinal < enemy * 0.82 + const draw = !win && !lose + + const year = opts.year + const month = opts.month + const names = opts.team.map((c) => c.name).join('、') + const lines: string[] = [] + lines.push(`—— ${opts.title} ——`) + lines.push(`${year}年${month}月,${names}遇上了【${opts.enemy.name}】。(敌势 ${enemy},我阵 ${teamFinal})`) + if (win) { + lines.push(`首战告捷:${w.rng.pick(WIN_DESC)}`) + } else if (lose) { + lines.push(`败象已成:${w.rng.pick(LOSE_DESC)}`) + } else { + lines.push(`来回缠斗:${w.rng.pick(DRAW_DESC)}`) + } + const loss: string[] = [] + for (const c of opts.team) { + if (!c.alive || c.state === 'wounded') continue + const severity = w.rng.next() + if (!win) { + if (severity < 0.1 && w.rng.chance(opts.risk * 0.08 + 0.02)) { + c.alive = false + c.deathYear = year + c.deathCause = `战殁于${opts.enemy.name}之手` + loss.push(`${c.name} 陨落`) + w.chronicle('death', `${c.name} 战殁于${opts.enemy.name},一身所学俱付尘烟。`, c.id, true) + } else if (severity < 0.45) { + c.health = Math.max(1, c.health - 40 - w.rng.int(0, 25)) + c.state = 'wounded' + loss.push(`${c.name} 重伤`) + } + } else if (w.rng.chance(0.12)) { + c.health = Math.max(1, c.health - 20 - w.rng.int(0, 15)) + if (c.health < 35) c.state = 'wounded' + loss.push(`${c.name} 轻伤`) + } + } + let loot: Record | undefined + if (win) { + loot = {} + const res = opts.risk > 0.8 ? { lingkuang: [20, 60], lingcao: [15, 40] } : { lingcao: [10, 30], lingkuang: [5, 20] } + for (const [k, r] of Object.entries(res)) { + const v = w.rng.int(r[0], r[1]) + loot[k] = v + w.state.family.inventory[k] = (w.state.family.inventory[k] ?? 0) + v + } + lines.push(`此战缴获:${Object.entries(loot).map(([k, v]) => `${itemName(k)} ×${v}`).join('、')}。`) + } + + const result: EncounterResult = { win, draw, lines, loot, losses: loss } + const log: BattleLog = { + id: w.seq(), + year, + month, + title: opts.title, + kind: opts.kind, + lines, + winner: win ? 'player' : draw ? 'none' : 'enemy', + loot, + losses: loss + } + w.battle(log) + return result +} + +function itemName(id: string): string { + const names: Record = { + lingcao: '灵草', + lingkuang: '灵矿', + beastcore: '兽核', + stones: '灵石' + } + return names[id] ?? id +} + +export function resolveRaid( + w: World, + npcId: string, + team: Character[] +): EncounterResult { + const npc = w.state.npcFamilies[npcId] + const def = npcById(npcId) + const enemy: EnemyDef = { + id: npcId, + name: `${npc.name}的劫掠队`, + realm: def.leaderRealm, + strength: 0.9, + icon: '袭', + desc: def.desc + } + const risk = 0.55 + const res = resolveEncounter(w, { + title: `${npc.name}来袭!`, + enemy, + risk, + team, + kind: 'war', + year: w.state.year, + month: w.state.month + }) + if (res.win) { + npc.relation = Math.min(60, npc.relation + 25) + w.state.family.reputation += 6 + w.chronicle('battle', `击退${npc.name}的犯境,家族声威大振。`, undefined, true) + } else if (!res.draw) { + npc.relation = Math.max(-100, npc.relation - 15) + const st = w.state.family.stones + const lostFew = Math.min(st, Math.round(st * 0.25)) + w.state.family.stones -= lostFew + if (lostFew > 0) w.log('bad', `宗族仓廪被劫掠,损失灵石 ${lostFew}。`) + } + return res +} + +export function rollWarbooty(w: World, loot: LootDef): Record { + const result: Record = {} + for (const [k, r] of Object.entries(loot.resources)) { + const v = w.rng.int(r[0], r[1]) + result[k] = v + w.state.family.inventory[k] = (w.state.family.inventory[k] ?? 0) + v + } + if (loot.artifactChance && w.rng.chance(loot.artifactChance)) { + const pool = ['weapon-fan', 'weapon-qi', 'weapon-ling'] + const a = w.rng.pick(pool) + w.state.family.inventory[a] = (w.state.family.inventory[a] ?? 0) + 1 + result[a] = 1 + } + if (loot.techniqueChance && w.rng.chance(loot.techniqueChance)) { + const t = w.rng.pick(TECHNIQUES) + w.state.family.techniques.push(t.id) + result['tech'] = 1 + } + return result +} diff --git a/src/renderer/game/engine/systems/cultivation.ts b/src/renderer/game/engine/systems/cultivation.ts new file mode 100644 index 0000000..40c4794 --- /dev/null +++ b/src/renderer/game/engine/systems/cultivation.ts @@ -0,0 +1,134 @@ +import { World } from '../world' +import { Character } from '../../types/domain' +import { ROOT_GRADES } from '../../data/elements' +import { masteryRateOfMajor } from '../../data/pacing' +import { techniqueById } from '../../data/techniques' +import { nextRealm, breakthroughBaseChance, realmDeathChance, describeRealm, MAJOR_ORDER } from '../../data/realms' +import { lifespanOf } from './lifecycle' +import { traitBonuses } from '../pcgen' +import { newCharacter } from '../pcgen' +import { MALE_GIVEN, FEMALE_GIVEN } from '../../core/names' + +export function monthlyRate(w: World, c: Character): number { + const st = w.state + let rate = 1 + rate *= 0.5 + c.perception * 0.1 + rate *= ROOT_GRADES[c.roots.grade]?.expBonus ?? 0.5 + const tech = techniqueById(c.techniqueId) + if (tech && c.realm.major !== 'mortal') rate *= 1 + tech.expBonus + else if (c.realm.major !== 'mortal') rate *= 0.65 + const buildings = st.family.buildings + const juling = buildings['juling'] ?? 0 + rate *= 1 + juling * 0.05 + if (c.state === 'meditation') { + rate *= 1.35 + const dongfu = buildings['dongfu'] ?? 0 + rate *= 1 + dongfu * 0.08 + } else if (c.state === 'expedition') { + rate *= 0.25 + } else if (c.state === 'wounded') { + rate *= c.health > 40 ? 0.5 : 0.15 + } + if (w.ageOf(c) < 8) rate *= 0.4 + if (w.ageOf(c) > 55) rate *= 0.7 + rate *= masteryRateOfMajor(c.realm.major) + return rate +} + +export function cultivationTick(w: World): void { + for (const c of Object.values(w.state.members)) { + if (!c.alive) continue + const rate = monthlyRate(w, c) + if (rate <= 0) continue + c.realmProgress = Math.min(100, c.realmProgress + rate) + if (c.realmProgress >= 100) { + const months = (w.state.year * 12 + w.state.month) - (c.lastBreakthroughAttempt ?? -999) + if (months >= 6 && w.rng.chance(perAttemptChance(w, c))) { + resolveBreakthrough(w, c, 0) + } + } + } +} + +export function perAttemptChance(w: World, c: Character): number { + const base = breakthroughBaseChance(c.realm) + const mind = c.mind * 0.008 + const traits = traitBonuses(c) + const headMind = (w.state.members[w.state.family.headId]?.mind ?? 5) * 0.004 + const healthMod = c.health > 60 ? 0.04 : -0.06 + const bless = w.state.family.flag['headBless'] ? 0.03 : 0 + return Math.min(0.95, Math.max(0.02, base + mind + traits.breakBonus + headMind + healthMod + bless)) +} + +export function resolveBreakthrough(w: World, c: Character, boost: number): void { + if (!c.alive || c.realmProgress < 100) return + const next = nextRealm(c.realm) + if (!next) return + const p = Math.min(0.95, Math.max(0.05, perAttemptChance(w, c) + boost)) + c.lastBreakthroughAttempt = w.state.year * 12 + w.state.month + + if (w.rng.chance(p)) { + const majorJump = next.major !== c.realm.major + c.realm = next + c.realmProgress = 0 + if (majorJump) { + c.health = 100 + } + const desc = describeRealm(next) + w.chronicle('breakthrough', `${c.name} 突破至【${desc}】。`, c.id, majorJump) + w.log('good', `✦ ${c.name} 突破到 ${desc}!`) + if (next.major === 'spirit') { + w.chronicle('breakthrough', `华夏震惊:${c.name} 踏入化神之列。`, c.id, true) + } + } else { + c.health = Math.max(1, c.health - 8 - w.rng.int(0, 10)) + let log = `${c.name} 冲击瓶颈失败,灵力紊乱受创。` + if (c.traits.includes('jizao') || c.traits.includes('yiqi')) { + c.health = Math.max(1, c.health - 14) + log = `${c.name} 强行突破遭反噬,气息受创。` + } + const majorIdx = MAJOR_ORDER.indexOf(c.realm.major) + if (majorIdx >= 3 && w.rng.chance(realmDeathChance(c.realm, c.mind))) { + c.alive = false + c.deathYear = w.state.year + c.deathCause = '突破走火' + w.chronicle('death', `${c.name} 妄图冲击瓶颈,走火入魔而陨。`, c.id, true) + w.log('bad', `☠ ${c.name} 突破走火,当场陨落。`) + return + } + if (c.health < 20) c.state = 'wounded' + const loss = 40 + w.rng.int(0, 25) + c.realmProgress = Math.max(0, Math.min(95, 100 - loss - boost * 60)) + w.log('bad', log) + } +} + +export function produceOffspring( + w: World, + opts: { + father: Character | null + mother: Character | null + generation: number + bornYear: number + surname: string + spouseHouse?: string + } +): Character { + const rng = w.rng + const newborn = newCharacter(rng, { + name: `${opts.surname}${rng.pick(rng.chance(0.52) ? MALE_GIVEN : FEMALE_GIVEN)}`, + gender: rng.chance(0.52) ? 'male' : 'female', + generation: opts.generation, + bornYear: opts.bornYear, + age: 0, + realm: { major: 'mortal', minor: 0 }, + father: opts.father ?? undefined, + mother: opts.mother ?? undefined + }) + if (opts.spouseHouse) newborn.spouseHouse = opts.spouseHouse + return newborn +} + +export function lifespanCheckPoint(w: World, c: Character): number { + return lifespanOf(w, c) +} diff --git a/src/renderer/game/engine/systems/diplomacy.ts b/src/renderer/game/engine/systems/diplomacy.ts new file mode 100644 index 0000000..2833e93 --- /dev/null +++ b/src/renderer/game/engine/systems/diplomacy.ts @@ -0,0 +1,93 @@ +import { World } from '../world' +import { npcById } from '../../data/npcs' +import { findEvent, fire } from './events' + +export function diplomacyTick(w: World): void { + const s = w.state + const drift = w.rng.chance(0.15) + for (const npc of Object.values(s.npcFamilies)) { + if (drift) { + if (npc.relation > 0) npc.relation -= 1 + else if (npc.relation < 0) npc.relation += 1 + } + if (npc.relation < -50) { + const last = (w.state.family.flag[`raidCD-${npc.id}`] as number | undefined) ?? 0 + if (s.year - last >= 2 && w.rng.chance(0.045)) { + fire(w, `ev-raid-${npc.id}`) + } + } + } +} + +export function yearGrowth(w: World): void { + const s = w.state + for (const npc of Object.values(s.npcFamilies)) { + const def = npcById(npc.id) + const [a, b] = def.powerGrowth + npc.power += w.rng.int(a, b) + } +} + +export function npcRelation(w: World, npcId: string): number { + return w.state.npcFamilies[npcId]?.relation ?? 0 +} + +export function giftNpc(w: World, npcId: string, stones: number): boolean { + const fam = w.state.family + if (stones <= 0 || fam.stones < stones) return false + fam.stones -= stones + const npc = w.state.npcFamilies[npcId] + const gain = Math.max(1, Math.round(stones / 12)) + npc.relation = Math.min(100, npc.relation + gain) + w.log('info', `厚礼送往${npc.name},两家关系 +${gain}。`) + return true +} + +export function makePeace(w: World, npcId: string): boolean { + const fam = w.state.family + const npc = w.state.npcFamilies[npcId] + if (fam.stones < 200) return false + fam.stones -= 200 + npc.relation = Math.max(npc.relation + 35, 0) + w.chronicle('diplomacy', `${npc.name}立下和约,两家罢兵互市。`, undefined, true) + w.log('good', `与${npc.name}言和。`) + return true +} + +export function marryNpcFamily(w: World, npcId: string): boolean { + const s = w.state + const fam = s.family + const npc = s.npcFamilies[npcId] + if (!npc || npc.relation < 25) return false + const eligible = w + .aliveMembers() + .filter((c) => w.ageOf(c) >= 18 && w.ageOf(c) <= 42 && c.state !== 'expedition') + .filter((c) => !c.spouseId) + if (eligible.length === 0) return false + const npcDef = npcById(npcId) + const candidate = w.rng.pick(eligible) + candidate.spouseHouse = npc.name + npc.relation += 20 + fam.reputation += 4 + w.chronicle('marriage', `${candidate.name}与${npc.name}联姻,两家绸缪通好。`, candidate.id, true) + w.log('good', `${candidate.name} 与${npc.name}联姻成功!每年或降麟儿。`) + return true +} + +export function arrangeWedding(w: World, aId: string, bId: string): boolean { + const a = w.memberById(aId) + const b = w.memberById(bId) + if (!a.alive || !b.alive || a.spouseId || b.spouseId) return false + if (a.gender === b.gender) return false + if (a.fatherId === b.fatherId && a.fatherId) return false + a.spouseId = b.id + b.spouseId = a.id + const aAge = w.ageOf(a) + const bAge = w.ageOf(b) + if (w.state.family.flag['tenants']) { + w.state.family.reputation += 1 + } + w.chronicle('marriage', `${a.name}(${aAge})与${b.name}(${bAge})拜堂成亲。`, a.id, true) + w.log('good', `${a.name} 与 ${b.name} 结为连理。`) + return true +} diff --git a/src/renderer/game/engine/systems/events.ts b/src/renderer/game/engine/systems/events.ts new file mode 100644 index 0000000..a367151 --- /dev/null +++ b/src/renderer/game/engine/systems/events.ts @@ -0,0 +1,290 @@ +import { World } from '../world' +import { Character } from '../../types/domain' +import { Cond, EffectDef, EventDef, EVENTS, MemberEffect } from '../../data/events' +import { MAJOR_ORDER } from '../../data/realms' +import { TECHNIQUES } from '../../data/techniques' +import { MISSIONS } from '../../data/secrets' +import { npcById } from '../../data/npcs' +import { resolveRaid } from './combat' +import { sendMission } from './missions' + +const ALL_EVENTS: EventDef[] = [...EVENTS] + +export function findEvent(id: string): EventDef | undefined { + return ALL_EVENTS.find((e) => e.id === id) ?? dynamicEventFor(id) +} + +export function dynamicEventFor(id: string): EventDef | undefined { + if (id.startsWith('ev-raid-')) { + const npcId = id.replace('ev-raid-', '') + const npc = npcById(npcId) + return { + id, + name: `${npc.name}来犯`, + category: 'major', + weight: 0, + text: `${npc.name}与贵庄积怨已久,如今撕破脸面,遣来劫掠队围门叫战。`, + options: [ + { label: '迎战!', hint: '大战一场,胜则大利,败则伤财', eff: { raid: { npcId } } }, + { label: '割地求和', hint: '灵石-250,关系+25', eff: { res: { stones: -250 }, relation: { [npcId]: 25 }, flag: { [npcId]: 'paid' } } }, + { label: '先议和缓兵', hint: '关系+10', eff: { relation: { [npcId]: 10 } } } + ] + } + } + return undefined +} + +export function matchesCond(w: World, cond?: Cond): boolean { + if (!cond) return true + const s = w.state + const fam = s.family + const alive = w.aliveMembers() + const adults = alive.filter((c) => w.ageOf(c) >= 16) + const head = s.members[fam.headId] + + if (cond.all && !cond.all.every((c) => matchesCond(w, c))) return false + if (cond.any && !cond.any.some((c) => matchesCond(w, c))) return false + if (cond.not && matchesCond(w, cond.not)) return false + if (cond.minYear !== undefined && s.year < cond.minYear) return false + if (cond.minGeneration !== undefined && fam.generation < cond.minGeneration) return false + if (cond.minHeadRealm !== undefined && (!head || MAJOR_ORDER.indexOf(head.realm.major) < MAJOR_ORDER.indexOf(cond.minHeadRealm as never))) return false + if (cond.minBuilding && (fam.buildings[cond.minBuilding.id] ?? 0) < cond.minBuilding.level) return false + if (cond.minRep !== undefined && fam.reputation < cond.minRep) return false + if (cond.maxRep !== undefined && fam.reputation > cond.maxRep) return false + if (cond.minResource && (fam.inventory[cond.minResource.id] ?? 0) < cond.minResource.n) return false + if (cond.minAdult !== undefined && adults.length < cond.minAdult) return false + if (cond.maxAdult !== undefined && adults.length > cond.maxAdult) return false + if (cond.minMembers !== undefined && alive.length < cond.minMembers) return false + if (cond.eligibleAdult !== undefined) { + const eligible = adults.filter((c) => !c.spouseId && !c.spouseHouse) + if (eligible.length < cond.eligibleAdult) return false + } + if (cond.hasMeditation && !alive.some((c) => c.state === 'meditation')) return false + if (cond.relation) { + const r = s.npcFamilies[cond.relation.npcId]?.relation ?? 0 + if (cond.relation.gt !== undefined && r <= cond.relation.gt) return false + if (cond.relation.lt !== undefined && r >= cond.relation.lt) return false + } + if (cond.flag) { + const v = fam.flag[cond.flag.key] + if (v !== cond.flag.eq) return false + } + if (cond.minTechCount !== undefined && fam.techniques.length < cond.minTechCount) return false + return true +} + +export function eventRoll(w: World): void { + const s = w.state + if (s.pendingEvent || s.eventQueue.length > 0) { + if (!s.pendingEvent && s.eventQueue.length > 0) { + fire(w, s.eventQueue.shift()!) + } + return + } + const roll = w.rng.next() + const category: 'daily' | 'major' | 'fate' | undefined = roll < 0.5 ? 'daily' : roll < 0.78 ? 'major' : roll < 0.86 ? 'fate' : undefined + if (!category) return + const candidates = ALL_EVENTS.filter( + (e) => + e.category === category && + !(e.once && s.completedEvents.includes(e.id)) && + matchesCond(w, e.cond) + ) + if (candidates.length === 0) return + const total = candidates.reduce((a, e) => a + e.weight, 0) + let r = w.rng.next() * total + for (const e of candidates) { + r -= e.weight + if (r <= 0) { + fire(w, e.id) + return + } + } +} + +export function fire(w: World, id: string): void { + w.state.pendingEvent = id + w.pendingEvent(id) +} + +export function applyEventChoice(w: World, eventId: string, optionIdx: number): void { + const s = w.state + const def = findEvent(eventId) + if (!def) { + s.pendingEvent = undefined + return + } + const opt = def.options[optionIdx] + if (opt) { + applyEffect(w, opt.eff) + if (def.once && !s.completedEvents.includes(def.id)) s.completedEvents.push(def.id) + } + s.pendingEvent = undefined +} + +// ---------------- effects ---------------- + +function pickMember(w: World, spec: MemberEffect): Character | Character[] { + const alive = w.aliveMembers() + if (alive.length === 0) return [] + const byTarget = (t: string): Character[] => { + const sorted = [...alive] + switch (t) { + case 'random': + return [w.rng.pick(sorted)] + case 'head': { + const h = w.state.members[w.state.family.headId] + return h && h.alive ? [h] : [] + } + case 'youngest': + return [sorted.sort((a, b) => w.ageOf(a) - w.ageOf(b))[0]] + case 'oldest': + return [sorted.sort((a, b) => w.ageOf(b) - w.ageOf(a))[0]] + case 'highestPerception': + return [sorted.sort((a, b) => b.perception - a.perception)[0]] + case 'highestPower': + return [sorted.sort((a, b) => rankPower(w, b) - rankPower(w, a))[0]] + case 'highestFortune': + return [sorted.sort((a, b) => b.fortune - a.fortune)[0]] + case 'all': + return sorted + default: + return [w.rng.pick(sorted)] + } + } + return byTarget(spec.target) +} + +export function rankPower(w: World, c: Character): number { + const order = ['mortal', 'qi', 'foundation', 'core', 'nascent', 'spirit'] + return order.indexOf(c.realm.major) * 10 + c.realm.minor +} + +function applyEffect(w: World, eff: EffectDef): void { + const s = w.state + const fam = s.family + + if (eff.res) { + for (const [k, v] of Object.entries(eff.res)) { + if (k === 'stones') fam.stones += v + else fam.inventory[k] = Math.max(0, (fam.inventory[k] ?? 0) + v) + } + } + if (eff.pillGain) { + for (const [k, v] of Object.entries(eff.pillGain)) fam.inventory[k] = (fam.inventory[k] ?? 0) + v + } + if (eff.rep) { + fam.reputation += eff.rep + if (Math.abs(eff.rep) >= 4) w.log(eff.rep > 0 ? 'good' : 'bad', `家族声望${eff.rep > 0 ? '上升' : '下跌'}${Math.abs(eff.rep)}。`) + } + if (eff.relation) { + for (const [k, v] of Object.entries(eff.relation)) { + const npc = s.npcFamilies[k] + if (npc) npc.relation = Math.max(-100, Math.min(100, npc.relation + v)) + } + } + if (eff.addBuilding && !fam.buildings[eff.addBuilding]) { + fam.buildings[eff.addBuilding] = 1 + } + if (eff.flag) { + Object.assign(fam.flag, eff.flag) + } + if (eff.techniqueChance && w.rng.chance(eff.techniqueChance)) { + const t = w.rng.pick(TECHNIQUES) + if (!fam.techniques.includes(t.id)) { + fam.techniques.push(t.id) + w.log('good', `得《${t.name}》残篇,录入藏书阁。`) + } + } + if (eff.artifactChance && w.rng.chance(eff.artifactChance)) { + const a = w.rng.pick(['weapon-fan', 'weapon-qi', 'weapon-ling']) + fam.inventory[a] = (fam.inventory[a] ?? 0) + 1 + w.log('good', '库中多了一件法器。') + } + if (eff.addTech) { + if (!fam.techniques.includes(eff.addTech)) fam.techniques.push(eff.addTech) + } + if (eff.memberBy) { + const targets = pickMember(w, eff.memberBy) + const by = eff.memberBy.by + const n = eff.memberBy.n ?? 1 + const list = Array.isArray(targets) ? targets : [targets] + for (const c of list) { + if (!c.alive) continue + switch (by) { + case 'exp': + c.realmProgress = Math.min(100, c.realmProgress + n) + w.log('info', `${c.name} 感悟顿生,修为精进。`) + break + case 'wound': + c.health = Math.max(1, c.health - 20 - n) + if (c.health < 35) c.state = 'wounded' + w.log('bad', `${c.name} 因此事负伤。`) + break + case 'heal': + c.health = Math.min(100, c.health + 20) + break + case 'breakthrough': + c.realmProgress = 100 + break + case 'fatal': { + if (w.rng.chance(0.35)) { + c.alive = false + c.deathYear = s.year + c.deathCause = '遭遇不测' + w.chronicle('death', `${c.name} 突遭不测,殒命于家宅之内。`, c.id, true) + } else { + c.health = Math.max(1, c.health - 60) + c.state = 'wounded' + } + break + } + case 'repGain': + fam.reputation += 2 + break + case 'inspire': + c.realmProgress = Math.min(100, c.realmProgress + n) + break + case 'loot': + c.fortune = Math.min(12, c.fortune + n) + break + case 'madness': + c.mind = Math.max(1, c.mind - 1) + c.health = Math.max(30, c.health - 10) + break + case 'genius': + c.perception = Math.min(10, c.perception + 1) + c.mind = Math.min(10, c.mind + 1) + break + } + } + } + if (eff.mission) { + const def = MISSIONS.find((m) => m.id === eff.mission) + if (def) { + const squad = w + .aliveMembers() + .filter((c) => w.ageOf(c) >= 16 && c.state !== 'expedition' && c.realm.major !== 'mortal') + .sort((a, b) => rankPower(w, b) - rankPower(w, a)) + .slice(0, def.maxMembers) + if (squad.length >= def.minMembers) { + sendMission(w, def.id, squad.map((c) => c.id)) + w.log('info', `家族闻讯而动,遣人奔赴【${def.name}】。`) + } + } + } + if (eff.raid) { + const npc = s.npcFamilies[eff.raid.npcId] + if (npc) { + const team = w + .aliveMembers() + .filter((c) => w.ageOf(c) >= 16 && c.state !== 'expedition') + .sort((a, b) => rankPower(w, b) - rankPower(w, a)) + .slice(0, 4) + if (team.length > 0) { + resolveRaid(w, eff.raid.npcId, team) + fam.flag[`raidCD-${eff.raid.npcId}`] = s.year + } + } + } +} diff --git a/src/renderer/game/engine/systems/lifecycle.ts b/src/renderer/game/engine/systems/lifecycle.ts new file mode 100644 index 0000000..5758b7f --- /dev/null +++ b/src/renderer/game/engine/systems/lifecycle.ts @@ -0,0 +1,47 @@ +import { World } from '../world' +import { MAJORS } from '../../data/realms' +import { calcLifespan } from '../pcgen' + +export function lifespanOf(w: World, c: { realm: { major: keyof typeof MAJORS }; physique: number }): number { + return calcLifespan(c.realm.major, c.physique) +} + +export function deathTick(w: World): void { + for (const c of Object.values(w.state.members)) { + if (!c.alive) continue + const age = w.ageOf(c) + const span = lifespanOf(w, c) + const softCap = span * 0.85 + let p = 0 + if (age >= span) p = 0.35 + else if (age >= softCap) { + const t = (age - softCap) / (span - softCap) + p = Math.min(0.3, Math.pow(t, 5) * 0.9) + } + if (c.health < 30) p += 0.18 + if (age < 2) p = Math.max(p, 0.02) + if (p > 0 && w.rng.chance(p)) { + c.alive = false + c.deathYear = w.state.year + const cause = age < 2 ? '幼夭' : c.health < 30 ? '伤势不治' : '寿元将尽' + c.deathCause = cause + w.chronicle('death', `${c.name} 辞世,年 ${age}。${age < 2 ? '族人无不痛惜。' : c.health < 30 ? '临终前仍在牵挂家族。' : '族人焚香送别。'}`, c.id, true) + w.log('bad', `${c.name}(${age}岁)${cause}。`) + } + } +} + +export function woundHealTick(w: World): void { + for (const c of Object.values(w.state.members)) { + if (!c.alive) continue + if (c.state === 'wounded') { + c.health = Math.min(100, c.health + 12 + c.physique) + if (c.health >= 95) { + c.state = 'idle' + w.log('info', `${c.name} 伤势痊愈。`) + } + } else if (c.health < 100) { + c.health = Math.min(100, c.health + 2 + c.physique * 0.5) + } + } +} diff --git a/src/renderer/game/engine/systems/marriage.ts b/src/renderer/game/engine/systems/marriage.ts new file mode 100644 index 0000000..9237540 --- /dev/null +++ b/src/renderer/game/engine/systems/marriage.ts @@ -0,0 +1,112 @@ +import { World } from '../world' +import { Character } from '../../types/domain' +import { produceOffspring } from './cultivation' +import { yearGrowth } from './diplomacy' +import { MALE_GIVEN, FEMALE_GIVEN } from '../../core/names' + +export function yearStartMarriage(w: World): void { + yearGrowth(w) + const s = w.state + const fam = s.family + const zongci = fam.buildings['zongci'] ?? 0 + const birthBase = 0.34 + zongci * 0.03 + (fam.difficulty === 'easy' ? 0.06 : fam.difficulty === 'hard' ? -0.06 : 0) + + const couples = buildCouples(w) + + // 族内夫妇 + for (const couple of couples.internal) { + const [a, b] = couple + const father = a.gender === 'male' ? a : b + const mother = a.gender === 'male' ? b : a + const fatherAge = w.ageOf(father) + const motherAge = w.ageOf(mother) + if (fatherAge < 18 || fatherAge > 52 || motherAge < 16 || motherAge > 46) continue + const p = birthBase * (0.75 + mother.physique * 0.05) + if (!w.rng.chance(p)) continue + const gen = Math.max(father.generation, mother.generation) + 1 + const first = produceOffspring(w, { father, mother, generation: gen, bornYear: s.year, surname: fam.surname }) + w.addMember(first, father, mother) + let note = `${fam.surname}氏新增一员,名唤${first.name},生年 ${s.year}。` + if (w.rng.chance(0.03)) { + const twin = produceOffspring(w, { father, mother, generation: gen, bornYear: s.year, surname: fam.surname }) + w.addMember(twin, father, mother) + note += ` 双生之喜!双子名唤${twin.name}。` + } + w.chronicle('birth', note, first.id, true) + w.log('good', `${fam.surname}家诞下新丁:${first.name}。`) + } + + // 联姻外孙来投 + for (const member of Object.values(s.members)) { + if (!member.alive || !member.spouseHouse) continue + if (member.gender !== 'female') continue + const age = w.ageOf(member) + if (age < 18 || age > 44) continue + if (!w.rng.chance(0.16)) continue + const gen = member.generation + 1 + const house = member.spouseHouse + const child = produceOffspring(w, { father: null, mother: null, generation: gen, bornYear: s.year, surname: fam.surname }) + child.spouseHouse = house + w.addMember(child) + w.chronicle('birth', `${member.name}自${house}携幼子归来,名唤${child.name}。`, child.id, true) + w.log('info', `${member.name} 领着小辈回门投亲。`) + } + + // 媒人撮合族内婚(含续弦与再醮) + const isWidowed = (c: Character): boolean => { + if (!c.spouseId) return false + const sp = w.state.members[c.spouseId] + return !!sp && !sp.alive + } + const men = w + .aliveMembers() + .filter((c) => c.gender === 'male' && c.state !== 'expedition' && (isWidowed(c) || !c.spouseId)) + .filter((c) => { + const age = w.ageOf(c) + return age >= 18 && age <= 48 + }) + const women = w + .aliveMembers() + .filter((c) => c.gender === 'female' && c.state !== 'expedition' && (isWidowed(c) || !c.spouseId)) + .filter((c) => { + const age = w.ageOf(c) + return age >= 16 && age <= 42 + }) + for (const m of men) { + if (w.rng.chance(0.28) && women.length > 0) { + const candidate = women.filter( + (x) => !(x.fatherId && x.fatherId === m.fatherId) && x !== m && !x.children.includes(m.id) && !m.children.includes(x.id) + ) + if (candidate.length === 0) continue + const bride = w.rng.pick(candidate) + m.spouseId = bride.id + bride.spouseId = m.id + w.chronicle('marriage', `${m.name}与${bride.name}缔结连理。`, m.id, true) + w.log('info', `${m.name} 与 ${bride.name} 成婚。`) + const idx = women.indexOf(bride) + if (idx >= 0) women.splice(idx, 1) + } + } + + fam.generation = Math.max( + fam.generation, + ...Object.values(s.members).filter((c) => c.alive).map((c) => c.generation) + ) +} + +function buildCouples(w: World): { internal: [Character, Character][]; cross: Character[] } { + const internal: [Character, Character][] = [] + const cross: Character[] = [] + for (const member of Object.values(w.state.members)) { + if (!member.alive || !member.spouseId) continue + const spouse = w.state.members[member.spouseId] + if (!spouse?.alive) continue + const key = [member.id, spouse.id].sort().join('|') + if (internal.some(([a, b]) => [a.id, b.id].sort().join('|') === key)) continue + internal.push([member, spouse]) + } + for (const member of Object.values(w.state.members)) { + if (member.alive && member.spouseHouse) cross.push(member) + } + return { internal, cross } +} diff --git a/src/renderer/game/engine/systems/missions.ts b/src/renderer/game/engine/systems/missions.ts new file mode 100644 index 0000000..05100ad --- /dev/null +++ b/src/renderer/game/engine/systems/missions.ts @@ -0,0 +1,151 @@ +import { World } from '../world' +import { MissionState } from '../../types/domain' +import { missionById, MissionDef, ENEMIES } from '../../data/secrets' +import { resolveEncounter, rollWarbooty } from './combat' +import { techniqueById } from '../../data/techniques' +import { describeRealm } from '../../data/realms' + +export function missionTick(w: World): void { + const alive = w.state.missions.filter((m) => !m.done) + for (const m of alive) { + m.stageMonth++ + + if (m.stageMonth < 2) continue + const def = missionById(m.defId) + const stage = def.stages[m.stage] + if (!stage || m.stageMonth < stage.months) continue + + if (stage.kind === 'event') { + const good = w.rng.chance(0.6) + if (good) { + m.log.push(`${m.stageMonth}月:${stage.title}——${stage.text?.safe ?? '安然无事。'}`) + if (w.rng.chance(0.2)) { + const squad = squadOf(w, m) + squad.forEach((c) => (c.realmProgress = Math.min(100, c.realmProgress + 4))) + m.log.push('途中参悟,众人皆有精进。') + } + } else { + m.log.push(`${m.stageMonth}月:${stage.title}——${stage.text?.bad ?? '遭遇凶险。'}`) + const squad = squadOf(w, m) + const victim = w.rng.pick(squad) + victim.health = Math.max(1, victim.health - 25 - w.rng.int(0, 15)) + if (victim.health < 35) victim.state = 'wounded' + } + } else if (stage.kind === 'resource') { + const loot = rollWarbooty(w, stage.loot ?? def.completionLoot) + m.log.push(`${stage.title}:收获 ${lootText(loot)}。`) + } else if (stage.kind === 'combat' || stage.kind === 'boss') { + const enemy = ENEMIES.find((e) => e.id === stage.enemyId) ?? ENEMIES[0] + const squad = squadOf(w, m) + const res = resolveEncounter(w, { + title: `${def.name} · ${stage.title}`, + enemy, + risk: def.risk * (stage.kind === 'boss' ? 1.15 : 1), + team: squad, + kind: 'scout', + year: w.state.year, + month: w.state.month + }) + const line = res.win ? '战而胜之,征程继续!' : res.draw ? '僵持之后双方罢手,队伍休整再进。' : '不敌,只得暂避锋芒。' + m.log.push(line) + if (!res.win) { + if (stage.kind === 'boss') { + m.done = true + m.result = res.draw ? 'stalemate' : 'retreat' + m.log.join(' ') + w.chronicle('exploration', `${def.name}探路不遂,${resultText(m)}。`, undefined, false) + } + } + } + + m.stage++ + m.stageMonth = 0 + if (m.stage >= def.stages.length && !m.done) { + m.done = true + m.result = 'success' + const total = rollWarbooty(w, def.completionLoot) + m.log.push(`凯旋而归,清点战利:${lootText(total)}。`) + const survivors = squadOf(w, m).filter((c) => c.alive).map((c) => c.name).join('、') + w.chronicle( + 'exploration', + `${survivors} 圆满完成【${def.name}】之行。`, + undefined, + true + ) + w.log('good', `${def.name} 探索归来,获得丰厚收获。`) + } + } +} + +function squadOf(w: World, m: MissionState) { + return m.memberIds.map((id) => w.memberById(id)).filter((c) => c.alive) +} + +function lootText(loot: Record): string { + const names: Record = { + lingcao: '灵草', + lingkuang: '灵矿', + beastcore: '兽核', + stones: '灵石', + 'weapon-fan': '凡器', + 'weapon-qi': '法器', + 'weapon-ling': '灵器', + 'pill-qiyuan': '聚气丹', + 'pill-ningyuan': '凝元丹', + tech: '功法' + } + return Object.entries(loot) + .map(([k, v]) => `${names[k] ?? k}×${v}`) + .join('、') +} + +function resultText(m: MissionState): string { + if (m.result === 'success') return '平安返回' + if (m.result === 'retreat') return '败退而回' + return '铩羽归来' +} + +export function canSendMission(w: World, def: MissionDef, members: string[]): boolean { + if (members.length < def.minMembers || members.length > def.maxMembers) return false + for (const id of members) { + const c = w.memberById(id) + if (!c.alive || c.state === 'expedition') return false + } + return w.state.missions.filter((m) => !m.done).length < 3 +} + +export function sendMission(w: World, defId: string, members: string[]): boolean { + const def = missionById(defId) + if (!canSendMission(w, def, members)) return false + const m: MissionState = { + id: w.seq(), + defId, + memberIds: members, + startYear: w.state.year, + startMonth: w.state.month, + stage: 0, + stageMonth: 0, + log: [`冬衣已备,饯行酒干,众人于 ${w.state.year} 年 ${w.state.month} 月出发。`], + done: false + } + members.forEach((id) => { + const c = w.memberById(id) + c.state = 'expedition' + }) + w.state.missions.push(m) + w.state.family.missionIds.push(m.id) + w.log('info', `队伍出发探索【${def.name}】。`) + return true +} + +export function recallAll(w: World, missionId: string): void { + const m = w.state.missions.find((x) => x.id === missionId) + if (!m || m.done) return + m.done = true + m.result = 'recall' + m.memberIds.forEach((id) => { + const c = w.memberById(id) + if (c.alive) c.state = 'idle' + }) + w.log('info', '探索队伍奉命返家。') +} diff --git a/src/renderer/game/engine/systems/production.ts b/src/renderer/game/engine/systems/production.ts new file mode 100644 index 0000000..991832d --- /dev/null +++ b/src/renderer/game/engine/systems/production.ts @@ -0,0 +1,51 @@ +import { World } from '../world' + +export function productionTick(w: World): void { + const fam = w.state.family + const inv = fam.inventory + const parts: string[] = [] + const lvl = (b: string) => fam.buildings[b] ?? 0 + + const lingtian = lvl('lingtian') + const yaoyuan = lvl('yaoyuan') + const lingkuang = lvl('lingkuang') + const fangshi = lvl('fangshi') + const lingshou = lvl('lingshou') + + if (lingtian > 0) { + const v = 10 * lingtian + inv.lingcao = (inv.lingcao ?? 0) + v + parts.push(`灵田+${v}灵草`) + } + if (yaoyuan > 0) { + const v = 5 * yaoyuan + inv.lingcao = (inv.lingcao ?? 0) + v + parts.push(`药园+${v}药草`) + if (yaoyuan >= 3) { + inv.beastcore = (inv.beastcore ?? 0) + 1 + parts.push('药园+1兽核') + } + } + if (lingkuang > 0) { + const v = 8 * lingkuang + inv.lingkuang = (inv.lingkuang ?? 0) + v + parts.push(`灵矿+${v}灵矿`) + } + if (fangshi > 0) { + const v = 55 * fangshi + fam.stones += v + parts.push(`坊市+${v}灵石`) + } + if (lingshou > 0 && w.rng.chance(0.35)) { + inv.beastcore = (inv.beastcore ?? 0) + 1 + parts.push('灵兽园+1兽核') + } + + void parts + + if (w.state.month % 3 === 0) { + const drift = w.rng.between(-0.04, 0.04) + const cur = typeof fam.flag['priceMult'] === 'number' ? (fam.flag['priceMult'] as number) : 1 + fam.flag['priceMult'] = Math.max(0.78, Math.min(1.25, cur + drift)) + } +} diff --git a/src/renderer/game/engine/world.ts b/src/renderer/game/engine/world.ts new file mode 100644 index 0000000..ebcca91 --- /dev/null +++ b/src/renderer/game/engine/world.ts @@ -0,0 +1,312 @@ +import { + BattleLog, + Character, + ChronicleEntry, + GameState, + Id, + LogItem, + Realm +} from '../types/domain' +import { Rng } from '../core/rng' +import { BUILDINGS } from '../data/buildings' +import { productionTick } from './systems/production' +import { deathTick, woundHealTick } from './systems/lifecycle' +import { cultivationTick, resolveBreakthrough } from './systems/cultivation' +import { missionTick } from './systems/missions' +import { eventRoll, applyEventChoice } from './systems/events' +import { diplomacyTick } from './systems/diplomacy' +import { yearStartMarriage } from './systems/marriage' +import { createWorldState, findInheritor } from './creation' +import { combatPowerOf } from './systems/combat' + +export type LogKind = LogItem['kind'] + +export interface WorldEventBus { + onLog(kind: LogKind, text: string): void + onChronicle(entry: ChronicleEntry, important: boolean): void + onBattle(log: BattleLog): void + onPendingEvent(id: string): void + onGameOver(reason: string, year: number): void +} + +export class World { + state: GameState + rng: Rng + out: WorldEventBus[] + + constructor(state: GameState, out: WorldEventBus[] = []) { + this.state = state + this.rng = new Rng(state.rng) + this.out = out + } + + seq(): Id { + this.state.seq++ + return `x${this.state.seq.toString(36)}` + } + + syncRng(): void { + this.state.rng = this.rng.getState() + } + + log(kind: LogKind, text: string): void { + this.out.forEach((o) => o.onLog(kind, text)) + } + + chronicle(cat: ChronicleEntry['category'], text: string, memberId?: Id, important = false): void { + const entry: ChronicleEntry = { + id: this.seq(), + year: this.state.year, + month: this.state.month, + category: cat, + text, + memberId, + important + } + this.state.chronicle.push(entry) + this.out.forEach((o) => o.onChronicle(entry, important)) + } + + battle(log: BattleLog): void { + this.state.battles.push(log) + this.out.forEach((o) => o.onBattle(log)) + } + + pendingEvent(id: string): void { + this.out.forEach((o) => o.onPendingEvent(id)) + } + + gameOver(reason: string, year: number): void { + this.state.gameOver = { year, reason } + this.out.forEach((o) => o.onGameOver(reason, year)) + } + + memberById(id: Id): Character { + const c = this.state.members[id] + if (!c) throw new Error(`member not found ${id}`) + return c + } + + aliveMembers(): Character[] { + return Object.values(this.state.members).filter((c) => c.alive) + } + + ageOf(c: Character): number { + return this.state.year - c.bornYear + } + + head(): Character { + return this.memberById(this.state.family.headId) + } + + advanceMonth(): void { + const s = this.state + s.month++ + if (s.month > 12) { + s.month = 1 + s.year++ + this.yearStart() + } + s.totalTicks++ + + productionTick(this) + deathTick(this) + woundHealTick(this) + if (this.aliveMembers().length > 0) { + cultivationTick(this) + missionTick(this) + eventRoll(this) + diplomacyTick(this) + } + this.checkHead() + } + + private yearStart(): void { + yearStartMarriage(this) + } + + reputationDrift(): void { + const cur = this.state.family.reputation + const drift = cur > 0 ? -1.5 : cur < 0 ? 1.2 : 0 + if (drift !== 0) this.state.family.reputation = Math.round(cur + drift) + } + + totalFamilyReputation(): number { + return this.state.family.reputation + } + + private checkHead(): void { + const s = this.state + if (s.gameOver) return + const headId = s.family.headId + if (!headId) return + const head = this.memberById(headId) + if (head.alive) return + const heir = findInheritor(this) + if (heir) { + this.assignHead(heir.id, true) + } else if (this.aliveMembers().length === 0) { + this.gameOver('满门凋零,香火断绝', s.year) + } + } + + // ==================== player actions ==================== + + assignHead(id: Id, silent = false): void { + const c = this.memberById(id) + if (!c.alive) return + if (this.state.family.headId && !silent) { + const old = this.memberById(this.state.family.headId) + old.isHead = false + } else { + const oldId = this.state.family.headId + if (oldId && this.state.members[oldId]) this.state.members[oldId].isHead = false + } + c.isHead = true + this.state.family.headId = id + if (!silent) { + this.chronicle('misc', `${c.name} 继任为家主。`, c.id, true) + this.log('info', `${c.name} 继任为家主。`) + } + } + + setMeditation(id: Id, on: boolean): void { + const c = this.memberById(id) + if (!c.alive || c.state === 'expedition') return + c.state = on ? 'meditation' : 'idle' + } + + giveTechnique(memberId: Id, techId: string): void { + const c = this.memberById(memberId) + c.techniqueId = techId + } + + teachTechnique(techId: string, cost: number): boolean { + const fam = this.state.family + if (fam.techniques.includes(techId)) return false + if (fam.stones < cost) return false + fam.stones -= cost + fam.techniques.push(techId) + this.log('info', `藏书阁续得《${techId}》,译作一名。`) + return true + } + + equip(memberId: Id, artifact: string): void { + const c = this.memberById(memberId) + if (!c.alive) return + c.equipment = artifact + } + + takePill(memberId: Id, pill: string): void { + const c = this.memberById(memberId) + const inv = this.state.family.inventory + if (!c.alive || (inv[pill] ?? 0) <= 0) return + inv[pill] = inv[pill]! - 1 + if (pill === 'pill-pojing') { + if (c.realmProgress >= 100) { + this.resolveBottleneck(c, 0.22) + } else { + this.memberById(memberId).realmProgress = Math.min(100, c.realmProgress + 20) + this.log('info', `${c.name} 服下破境丹,灵力充盈。`) + } + } else { + const pct = pill === 'pill-qiyuan' ? 18 : 30 + c.realmProgress = Math.min(100, c.realmProgress + pct) + this.log('info', `${c.name} 服下丹药,修为精进。`) + } + } + + assistedBreakthrough(id: Id): void { + const c = this.memberById(id) + if (!c.alive || c.realmProgress < 100) return + this.resolveBottleneck(c, 0.06 + this.head().mind * 0.005) + } + + private resolveBottleneck(c: Character, boost: number): void { + resolveBreakthrough(this, c, boost) + } + + build(id: string): boolean { + const fam = this.state.family + const def = BUILDINGS[id] + if (!def) return false + if (fam.buildings[id]) return false + const cost = def.upgradeCost(1) + if (fam.stones < cost.stones) return false + fam.stones -= cost.stones + fam.buildings[id] = 1 + this.chronicle('building', `建成「${def.name}」。`, undefined, true) + this.log('info', `建成「${def.name}」。`) + return true + } + + upgrade(id: string): boolean { + const fam = this.state.family + const def = BUILDINGS[id] + const lvl = fam.buildings[id] + if (!def || !lvl || lvl >= def.maxLevel) return false + const cost = def.upgradeCost(lvl + 1) + if (fam.stones < cost.stones || (fam.inventory['lingkuang'] ?? 0) < cost.lingkuang) return false + fam.stones -= cost.stones + fam.inventory['lingkuang'] -= cost.lingkuang + fam.buildings[id] = lvl + 1 + this.log('info', `「${def.name}」升至 ${lvl + 1} 级。`) + return true + } + + craftPill(kind: 'qiyuan' | 'ningyuan'): boolean { + const fam = this.state.family + const lvl = fam.buildings['danfang'] + if (!lvl) return false + const cost = kind === 'qiyuan' + ? { lingcao: 15, beastcore: 0, stones: 20 } + : { lingcao: 25, beastcore: 4, stones: 60 } + if ((fam.inventory['lingcao'] ?? 0) < cost.lingcao) return false + if ((fam.inventory['beastcore'] ?? 0) < cost.beastcore) return false + if (fam.stones < cost.stones) return false + fam.inventory['lingcao'] -= cost.lingcao + fam.inventory['beastcore'] -= cost.beastcore + fam.stones -= cost.stones + fam.inventory[kind === 'qiyuan' ? 'pill-qiyuan' : 'pill-ningyuan'] = + (fam.inventory[kind === 'qiyuan' ? 'pill-qiyuan' : 'pill-ningyuan'] ?? 0) + 1 + this.log('info', `丹房炼成一枚${kind === 'qiyuan' ? '聚气丹' : '凝元丹'}。`) + return true + } + + addMember(c: Character, father?: Character, mother?: Character): void { + const s = this.state + if (father || mother) { + if (father) { + c.fatherId = father.id + father.children.push(c.id) + } + if (mother) mother.children.push(c.id) + } + c.id = c.id || this.seq() + s.members[c.id] = c + } + + familyPower(): number { + const fam = this.state.family + const bonus = 1 + (fam.buildings['yanwu'] ?? 0) * 0.04 + (fam.buildings['lingshou'] ?? 0) * 0.05 + const top = this.aliveMembers() + .map((c) => combatPowerOf(this, c)) + .sort((a, b) => b - a) + .slice(0, 4) + .reduce((a, b) => a + b, 0) + return Math.round(top * bonus) + } + + static create(opts: { seed: string; surname: string; familyName: string; motto: string; difficulty: 'easy' | 'normal' | 'hard' }): World { + const state = createWorldState(opts) + return new World(state) + } +} + +export function makeWorldFromSave(state: GameState): World { + return new World(state, []) +} + +export function applyChoice(world: World, eventId: string, optionIdx: number): void { + applyEventChoice(world, eventId, optionIdx) +} diff --git a/src/renderer/game/storage/db.ts b/src/renderer/game/storage/db.ts new file mode 100644 index 0000000..0223318 --- /dev/null +++ b/src/renderer/game/storage/db.ts @@ -0,0 +1,140 @@ +import { SaveSlot } from './slots' +import { GameState, SaveMeta, SnapshotMeta } from '../types/domain' +import type { SaveDbDriver } from './slots' + +// Adapter for @metona-team/metona-sqlark +// The driver interface keeps the engine swappable (testable in node too). +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyFactory = { create: (config: any) => Promise } +let MetonaSqlark: AnyFactory | null = null + +interface SqlarkLike { + query: (sql: string, params?: unknown[]) => Promise +} + +export function setSqlarkBackend(impl: AnyFactory): void { + MetonaSqlark = impl +} + +export async function makeDriver(): Promise { + const factory = await getSqlarkFactory() + return new SqlarkDriver(factory) +} + +class SqlarkDriver implements SaveDbDriver { + private db: SqlarkLike | null = null + + constructor(private factory: AnyFactory) {} + + async open(name: string): Promise { + if (this.db) return + this.db = (await this.factory.create({ + name, + mode: 'aria', + diskEngine: 'opfs', + aria: { walSyncMode: 'full', compression: true } + })) as SqlarkLike + } + + async close(): Promise { + this.db = null + } + + async run(sql: string, params: unknown[] = []): Promise { + if (!this.db) throw new Error('db not open') + await this.db.query(sql, params) + return null + } + + async all(sql: string, params: unknown[] = []): Promise { + if (!this.db) throw new Error('db not open') + const rows = (await this.db.query(sql, params)) as T[] + return rows ?? [] + } + + async exec(sql: string): Promise { + if (!this.db) throw new Error('db not open') + await this.db.query(sql) + return null + } +} + +const META_DB = 'cotyc-appmeta' + +let singletonManager: SlotManager | null = null +export function getSlotManager(): SlotManager { + if (!singletonManager) singletonManager = new SlotManager() + return singletonManager +} + +export async function getSqlarkFactory(): Promise { + if (!MetonaSqlark) { + const mod = await import('@metona-team/metona-sqlark') + MetonaSqlark = mod.MetonaSqlark ?? mod.MeSqlark + } + if (!MetonaSqlark) throw new Error('metona-sqlark not available') + return MetonaSqlark +} + +const slotCache = new Map() +export async function getSaveSlot(slot: number): Promise { + let s = slotCache.get(slot) + if (!s) { + const factory = await getSqlarkFactory() + s = new SaveSlot(slot, new SqlarkDriver(factory)) + slotCache.set(slot, s) + } + return s +} + +export class SlotManager { + private metaDb: SqlarkLike | null = null + + async ensureMeta(): Promise { + if (this.metaDb) return + this.metaDb = (await (await getSqlarkFactory()).create({ + name: META_DB, + mode: 'aria', + diskEngine: 'opfs', + aria: { walSyncMode: 'full' } + })) as SqlarkLike + await this.metaDb.query(`CREATE TABLE IF NOT EXISTS slots (slot number PRIMARY KEY, meta string)`) + } + + async listSlotMetas(): Promise<(SaveMeta | null)[]> { + await this.ensureMeta() + const rows = await this.metaDb!.query(`SELECT slot, meta FROM slots ORDER BY slot`) + const map = new Map() + for (const r of rows as { slot: number; meta: string }[]) { + try { + map.set(r.slot, JSON.parse(r.meta) as SaveMeta) + } catch { + map.set(r.slot, null as unknown as SaveMeta) + } + } + const out: (SaveMeta | null)[] = [] + for (let i = 1; i <= 4; i++) { + out.push(map.get(i) ?? null) + } + return out + } + + async updateSlotMeta(slot: number, meta: SaveMeta): Promise { + await this.ensureMeta() + const existing = await this.metaDb!.query(`SELECT slot FROM slots WHERE slot = ?`, [slot]) + const json = JSON.stringify(meta) + if (existing.length > 0) { + await this.metaDb!.query(`UPDATE slots SET meta = ? WHERE slot = ?`, [json, slot]) + } else { + await this.metaDb!.query(`INSERT INTO slots (slot, meta) VALUES (?, ?)`, [slot, json]) + } + } + + async removeSlotMeta(slot: number): Promise { + await this.ensureMeta() + await this.metaDb!.query(`DELETE FROM slots WHERE slot = ?`, [slot]) + } +} + + +export type { GameState, SaveMeta, SnapshotMeta } diff --git a/src/renderer/game/storage/slots.ts b/src/renderer/game/storage/slots.ts new file mode 100644 index 0000000..05e9eac --- /dev/null +++ b/src/renderer/game/storage/slots.ts @@ -0,0 +1,156 @@ +import { GameState, SaveMeta, SnapshotMeta, Id } from '../types/domain' + +const DB_PREFIX = 'cotyc-save-' + +export interface SaveDbDriver { + open(name: string): Promise + close(): Promise + run(sql: string, params?: unknown[]): Promise + all(sql: string, params?: unknown[]): Promise + exec(sql: string): Promise +} + +export interface TableDef { + name: string + cols: Record +} + +export class SaveSlot { + constructor( + public slot: number, + private driver: SaveDbDriver + ) {} + + private get name(): string { + return `${DB_PREFIX}${this.slot}` + } + + async open(): Promise { + await this.driver.open(this.name) + await this.ensureSchema() + } + + private async ensureSchema(): Promise { + const driver = this.driver + await driver.exec(`CREATE TABLE IF NOT EXISTS meta (key string PRIMARY KEY, value string)`) + await driver.exec(`CREATE TABLE IF NOT EXISTS snapshot (id string PRIMARY KEY, year number, month number, savedAt string, label string, data string)`) + await driver.exec(`CREATE TABLE IF NOT EXISTS chronicle (id string PRIMARY KEY, year number, month number, category string, important number, memberId string, text string)`) + } + + async saveState(state: GameState, label: string): Promise { + const id = `${state.year}.${state.month}.${Date.now().toString(36)}` + const json = JSON.stringify(state) + await this.driver.run( + `INSERT INTO snapshot (id, year, month, savedAt, label, data) VALUES (?, ?, ?, ?, ?, ?)`, + [id, state.year, state.month, new Date().toISOString(), label, json] + ) + const keep = 12 + const rows = await this.driver.all<{ id: string }>( + `SELECT id FROM snapshot ORDER BY year DESC, month DESC, savedAt DESC` + ) + if (rows.length > keep) { + const drop = rows.slice(keep).map((r) => r.id) + for (const d of drop) { + await this.driver.run(`DELETE FROM snapshot WHERE id = ?`, [d]) + } + } + return id + } + + async saveChronicle(chronicle: GameState['chronicle']): Promise { + const driver = this.driver + for (const e of chronicle) { + try { + const exists = await driver.all<{ id: string }>(`SELECT id FROM chronicle WHERE id = ?`, [e.id]) + if (exists.length > 0) continue + await driver.run( + `INSERT INTO chronicle (id, year, month, category, important, memberId, text) VALUES (?, ?, ?, ?, ?, ?, ?)`, + [e.id, e.year, e.month, e.category, e.important ? 1 : 0, e.memberId ?? null, e.text] + ) + } catch { + // chronicle table redundancy is best-effort + } + } + } + + async listSnapshots(): Promise { + const rows = await this.driver.all(`SELECT id, year, month, savedAt, label FROM snapshot ORDER BY year DESC, month DESC`) + return rows.map((r) => ({ id: r.id, year: r.year, month: r.month, savedAt: r.savedAt, label: r.label })) + } + + async loadState(id?: string): Promise { + const rows = await this.driver.all<{ data: string }>( + id ? `SELECT data FROM snapshot WHERE id = ?` : `SELECT data FROM snapshot ORDER BY rowid DESC LIMIT 1` + , id ? [id] : []) + if (!rows || rows.length === 0) return null + return JSON.parse(rows[0]!.data) as GameState + } + + async deleteSnapshot(id: string): Promise { + await this.driver.run(`DELETE FROM snapshot WHERE id = ?`, [id]) + } + + async getMeta(): Promise { + const rows = await this.driver.all<{ value: string }>(`SELECT value FROM meta WHERE key = 'meta'`) + if (!rows || rows.length === 0) return null + return JSON.parse(rows[0].value) as SaveMeta + } + + async setMeta(meta: SaveMeta): Promise { + const exists = await this.driver.all<{ key: string }>(`SELECT key FROM meta WHERE key = 'meta'`) + const json = JSON.stringify(meta) + if (exists.length > 0) { + await this.driver.run(`UPDATE meta SET value = ? WHERE key = 'meta'`, [json]) + } else { + await this.driver.run(`INSERT INTO meta (key, value) VALUES ('meta', ?)`, [json]) + } + } + + async exportAll(): Promise { + const state = await this.loadState() + const meta = await this.getMeta() + return JSON.stringify({ app: 'cotyc', schemaVersion: 1, meta, state }) + } + + async importAll(data: string): Promise { + try { + const parsed = JSON.parse(data) as { app?: string; schemaVersion?: number; state?: GameState } + if (!parsed.state) return false + await this.driver.exec(`DELETE FROM snapshot`) + await this.driver.exec(`DELETE FROM meta`) + await this.saveState(parsed.state, 'import') + const aliveCount = Object.values(parsed.state.members).filter((c) => c.alive).length + await this.setMeta(buildSimpleMeta(parsed.state, this.slot, aliveCount)) + return true + } catch { + return false + } + } + + async wipe(): Promise { + await this.driver.exec(`DELETE FROM snapshot`) + await this.driver.exec(`DELETE FROM meta`) + await this.driver.exec(`DELETE FROM chronicle`) + } +} + +export function buildSimpleMeta(state: GameState, slot: number, memberCount: number): SaveMeta { + return { + slot, + surname: state.family.surname, + name: state.family.name, + estate: state.family.estate, + year: state.year, + month: state.month, + generation: state.family.generation, + members: memberCount, + reputation: state.family.reputation, + updatedAt: new Date().toISOString(), + version: state.schemaVersion + } +} + +export function saveNameOf(meta: SaveMeta | null): string { + if (!meta) return '' + return `${meta.name} · ${meta.estate}` +} diff --git a/src/renderer/game/types/domain.ts b/src/renderer/game/types/domain.ts new file mode 100644 index 0000000..5a604ff --- /dev/null +++ b/src/renderer/game/types/domain.ts @@ -0,0 +1,182 @@ +export type Id = string +export type Gender = 'male' | 'female' + +export type Element = '金' | '木' | '水' | '火' | '土' + +export type RealmMajor = 'mortal' | 'qi' | 'foundation' | 'core' | 'nascent' | 'spirit' + +export interface Realm { + major: RealmMajor + minor: number +} + +export type CharState = 'idle' | 'meditation' | 'expedition' | 'wounded' | 'closed' + +export interface SpiritRoots { + grade: number + primary: Element + secondary: Element[] +} + +export interface Character { + id: Id + name: string + gender: Gender + generation: number + fatherId?: Id + motherId?: Id + spouseId?: Id + spouseHouse?: string + children: Id[] + bornYear: number + deathYear?: number + deathCause?: string + age: number + realm: Realm + realmProgress: number + roots: SpiritRoots + perception: number + physique: number + mind: number + charm: number + fortune: number + traits: string[] + techniqueId?: string + equipment?: string + state: CharState + health: number + alive: boolean + isHead?: boolean + isFounder?: boolean + lastBreakthroughAttempt?: number + monthProgress?: number +} + +export interface FamilyState { + surname: string + name: string + motto: string + crest: string + estate: string + yearFounded: number + generation: number + reputation: number + stones: number + inventory: Record + buildings: Record + techniques: string[] + missionIds: Id[] + headId: Id + difficulty: 'easy' | 'normal' | 'hard' + flag: Record +} + +export interface NpcFamilyState { + id: string + name: string + region: string + power: number + relation: number + allied: boolean + alliedSinceYear?: number + warCooldownYear?: number + raidCount: number +} + +export interface MissionState { + id: Id + defId: string + memberIds: Id[] + startYear: number + startMonth: number + stage: number + stageMonth: number + log: string[] + done: boolean + result?: string +} + +export interface ChronicleEntry { + id: Id + year: number + month: number + category: 'birth' | 'marriage' | 'death' | 'breakthrough' | 'battle' | 'trade' | 'diplomacy' | 'exploration' | 'building' | 'event' | 'misc' + text: string + memberId?: Id + important: boolean +} + +export interface BattleLog { + id: Id + year: number + month: number + title: string + kind: 'scout' | 'raid' | 'war' + lines: string[] + winner: 'player' | 'enemy' | 'none' + loot?: Record + losses: string[] +} + +export interface RngState { + a: number + b: number + c: number + d: number +} + +export interface GameOver { + year: number + reason: string +} + +export interface GameState { + schemaVersion: number + seed: string + rng: RngState + year: number + month: number + seq: number + family: FamilyState + members: Record + npcFamilies: Record + missions: MissionState[] + chronicle: ChronicleEntry[] + battles: BattleLog[] + eventQueue: string[] + pendingEvent?: string + completedEvents: string[] + flags: Record + gameOver?: GameOver + totalTicks: number +} + +export interface LogItem { + id: number + kind: 'info' | 'good' | 'bad' | 'war' | 'event' | 'chronicle' + text: string + year: number + month: number +} + +export interface SaveMeta { + slot: number + surname: string + name: string + estate: string + year: number + month: number + generation: number + members: number + reputation: number + updatedAt: string + version: number +} + +export interface SnapshotMeta { + id: string + year: number + month: number + savedAt: string + label: string +} diff --git a/src/renderer/index.html b/src/renderer/index.html new file mode 100644 index 0000000..742f9af --- /dev/null +++ b/src/renderer/index.html @@ -0,0 +1,16 @@ + + + + + + + 仙途家族志 + + +
+ + + diff --git a/src/renderer/main.tsx b/src/renderer/main.tsx new file mode 100644 index 0000000..3ef95ad --- /dev/null +++ b/src/renderer/main.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { createRoot } from 'react-dom/client' +import App from './App' +import './ui/styles.css' + +const root = document.getElementById('root') +if (root) { + createRoot(root).render( + + + + ) +} diff --git a/src/renderer/ui/components/BattleModal.tsx b/src/renderer/ui/components/BattleModal.tsx new file mode 100644 index 0000000..23b9be7 --- /dev/null +++ b/src/renderer/ui/components/BattleModal.tsx @@ -0,0 +1,35 @@ +import { useGameStore } from '../store' + +export function BattleModal() { + const battleView = useGameStore((s) => s.battleView) + if (!battleView) return null + + const winnerLabel = + battleView.winner === 'player' ? '✦ 我方得胜 ✦' : battleView.winner === 'enemy' ? '✕ 我方败北 ✕' : '◌ 平分秋色 ◌' + + return ( +
+
+
{battleView.title}
+
+ {battleView.year}年{battleView.month}月 · {winnerLabel} +
+
+ {battleView.lines.map((l, i) => ( +
{l}
+ ))} + {battleView.losses.length > 0 && ( +
+ 我方折损:{battleView.losses.join('、')} +
+ )} +
+
+ +
+
+
+ ) +} + +export default BattleModal diff --git a/src/renderer/ui/components/EventModal.tsx b/src/renderer/ui/components/EventModal.tsx new file mode 100644 index 0000000..9b329fa --- /dev/null +++ b/src/renderer/ui/components/EventModal.tsx @@ -0,0 +1,42 @@ +import { useGameStore } from '../store' +import { applyEventChoice } from '../../game/engine/systems/events' +import { eventCategoryName, EventDef } from '../../game/data/events' + +export function EventModal() { + const pendingEventId = useGameStore((s) => s.pendingEventId) + const pendingEventDef = useGameStore((s) => s.pendingEventDef) + const world = useGameStore((s) => s.world) + const bump = useGameStore((s) => s.bump) + const setSpeed = useGameStore((s) => s.setSpeed) + if (!pendingEventId || !pendingEventDef || !world) return null + + const choose = (idx: number) => { + applyEventChoice(world, pendingEventId, idx) + useGameStore.setState({ pendingEventId: undefined, pendingEventDef: undefined }) + bump() + // 重要事务后暂停推算,给玩家喘息 + const sp = useGameStore.getState().speed + if (sp > 1) setSpeed(1) + } + + return ( +
+
+
{pendingEventDef.name}
+
+ {eventCategoryName(pendingEventDef.category)} + {pendingEventDef.once && ' · 仅此一次'} +
+
{pendingEventDef.text}
+
+ {pendingEventDef.options.map((o, i) => ( + + ))} +
+
+
+ ) +} diff --git a/src/renderer/ui/components/LogFeed.tsx b/src/renderer/ui/components/LogFeed.tsx new file mode 100644 index 0000000..536d483 --- /dev/null +++ b/src/renderer/ui/components/LogFeed.tsx @@ -0,0 +1,17 @@ +import { useGameStore } from '../store' + +export function LogFeed() { + const feed = useGameStore((s) => s.logFeed) + const ref = useGameStore((s) => s.revision) + void ref + return ( +
+ {feed.slice().reverse().map((item) => ( +
+ {item.year}年{item.month}月 + {item.text} +
+ ))} +
+ ) +} diff --git a/src/renderer/ui/components/MemberCard.tsx b/src/renderer/ui/components/MemberCard.tsx new file mode 100644 index 0000000..a6ecdad --- /dev/null +++ b/src/renderer/ui/components/MemberCard.tsx @@ -0,0 +1,61 @@ +import { useGameStore } from '../store' +import { Character } from '../../game/types/domain' +import { describeRealm } from '../../game/data/realms' +import { describeRoots } from '../../game/data/elements' + +const STATE_LABEL: Record = { + idle: { label: '无事', cls: '' }, + meditation: { label: '闭关', cls: 'good' }, + expedition: { label: '出探', cls: 'warn' }, + wounded: { label: '养伤', cls: 'bad' } +} + +export function MemberCard({ c }: { c: Character }) { + const world = useGameStore((s) => s.world) + const setSelected = useGameStore((s) => s.setSelected) + if (!world) return null + const dead = !c.alive + const isHead = world.state.family.headId === c.id + const state = STATE_LABEL[c.state] + return ( +
setSelected(c.id)}> +
+
{world.state.family.surname}
+
+
+ {c.name} + {isHead && 家主} + {c.spouseHouse && 外亲} +
+
+ {dead + ? `殁于${c.deathYear}年 · ${c.deathCause ?? '寿终'}` + : `${describeRealm(c.realm)} · ${world.ageOf(c)}岁 · 修为${Math.floor(c.realmProgress)}%`} +
+
+
+
+ {describeRoots(c.roots)} + {!dead && c.realmProgress >= 100 && 瓶颈 · 可冲击} + {C_STATE[dead ? 'dead' : c.state] && ( + {STATE_LABEL[c.state]?.label ?? ''} + )} +
+ {!dead && ( +
+
+
+
+
+ )} +
+ ) +} + +const C_STATE: Record = { + idle: '', + meditation: 'good', + expedition: 'warn', + wounded: 'bad', + dead: 'bad' +} diff --git a/src/renderer/ui/components/MemberModal.tsx b/src/renderer/ui/components/MemberModal.tsx new file mode 100644 index 0000000..0fbeea1 --- /dev/null +++ b/src/renderer/ui/components/MemberModal.tsx @@ -0,0 +1,167 @@ +import { useGameStore } from '../store' +import { Character } from '../../game/types/domain' +import { describeRealm, nextRealm } from '../../game/data/realms' +import { describeRoots, ROOT_GRADE_NAMES } from '../../game/data/elements' +import { TRAITS } from '../../game/data/traits' +import { TECHNIQUES } from '../../game/data/techniques' +import { ITEMS, ARTIFACT_POWER } from '../../game/data/items' +import { combatPowerOf } from '../../game/engine/systems/combat' +import { lifespanOf } from '../../game/engine/systems/lifecycle' + +export function MemberModal({ member }: { member: Character }) { + const world = useGameStore((s) => s.world) + const setSelected = useGameStore((s) => s.setSelected) + const bump = useGameStore((s) => s.bump) + if (!world) return null + const w = world + const s = w.state + const next = nextRealm(member.realm) + const nextName = next ? describeRealm(next) : '已至巅峰' + const tech = TECHNIQUES.find((t) => t.id === member.techniqueId) + const power = member.alive ? combatPowerOf(w, member) : 0 + const lifespan = lifespanOf(w, member) + + return ( +
setSelected(undefined)}> +
e.stopPropagation()}> +
+ {member.name} + {!member.alive && · 殁} +
+
+ 第{member.generation}代 · {member.gender === 'male' ? '男' : '女'} · {w.ageOf(member)}岁 · 寿数约{lifespan} · {describeRoots(member.roots)} +
+ +
+
境界进度{describeRealm(member.realm)} {Math.floor(member.realmProgress)}%
+
下一关= 100 ? 'gold' : ''}>{nextName}
+
战力{Math.round(power)}
+
状态{member.state === 'meditation' ? '闭关' : member.state === 'expedition' ? '外出' : member.state === 'wounded' ? '养伤' : '无事'}
+
气血{Math.round(member.health)}
+
功法{tech ? `${tech.name}(${['黄', '玄', '地', '天', '仙'][tech.grade]}阶)` : member.realm.major === 'mortal' ? '—' : '未习功法'}
+
法宝{member.equipment ? `${ITEMS[member.equipment].name}(+${Math.round((ARTIFACT_POWER[member.equipment] ?? 0) * 100)}%)` : '—'}
+
姻亲{member.spouseId ? s.members[member.spouseId]?.name ?? '' : member.spouseHouse ?? '未婚'}
+
+ +
+
+
+ +
+
+
悟性{member.perception}/10
+
根骨{member.physique}/10
+
心性{member.mind}/10
+
魅力{member.charm}/10
+
气运{member.fortune}/12
+
品性{member.fortune ? (member.fortune > 7 ? '俊逸' : '端正') : '—'}
+
+
+ +
+
秉性
+ {member.traits.map((t) => ( + + {TRAITS[t]?.name ?? t} + + ))} + {member.traits.length === 0 && 平淡无奇} +
+ +
+
起居
+
+ {member.alive && member.state !== 'expedition' && ( + + )} + {member.alive && member.realmProgress >= 100 && member.realm.major !== 'spirit' && ( + + )} + {member.alive && ( + <> + + {member.realm.major !== 'mortal' && ( + + )} + {member.realmProgress >= 100 && member.realm.major !== 'mortal' && ( + + )} + + )} +
+
+ + {member.alive && ( +
+
传功(藏书阁所录)
+
+ {s.family.techniques.map((tid) => ( + + ))} + {s.family.techniques.length === 0 && 无典籍存库} +
+
+ )} + +
+ {member.children.length > 0 && ( + + 子女:{member.children.map((cid) => s.members[cid]?.name ?? '?').join('、')} + + )} + +
+
+
+ ) +} diff --git a/src/renderer/ui/components/index.ts b/src/renderer/ui/components/index.ts new file mode 100644 index 0000000..05e4a74 --- /dev/null +++ b/src/renderer/ui/components/index.ts @@ -0,0 +1,13 @@ +export { LogFeed } from './LogFeed' +export { EventModal } from './EventModal' +export { BattleModal } from './BattleModal' +export { MemberModal } from './MemberModal' +export { MemberCard } from './MemberCard' + +export const RES_INFO = [ + { id: 'stones', label: '灵石', icon: '石' }, + { id: 'lingcao', label: '灵草', icon: '草' }, + { id: 'lingkuang', label: '灵矿', icon: '矿' }, + { id: 'beastcore', label: '兽核', icon: '核' }, + { id: 'pill-qiyuan', label: '聚气丹', icon: '气' } +] diff --git a/src/renderer/ui/panels/ChroniclePanel.tsx b/src/renderer/ui/panels/ChroniclePanel.tsx new file mode 100644 index 0000000..6fdbbbc --- /dev/null +++ b/src/renderer/ui/panels/ChroniclePanel.tsx @@ -0,0 +1,59 @@ +import { useGameStore } from '../store' +import { useMemo, useState } from 'react' +import { ChronicleEntry } from '../../game/types/domain' + +const CAT_NAME: Record = { + birth: '诞庆', + marriage: '姻缘', + death: '祭丧', + breakthrough: '突破', + battle: '战事', + trade: '商贸', + diplomacy: '外交', + exploration: '寻宝', + building: '营造', + event: '奇遇', + misc: '家常' +} + +export default function ChroniclePanel() { + const world = useGameStore((s) => s.world) + const revision = useGameStore((s) => s.revision) + void revision + const [filter, setFilter] = useState('all') + const entries = useMemo(() => { + if (!world) return [] + const all = [...world.state.chronicle] + const filtered = filter === 'all' ? all : all.filter((e) => e.category === filter) + return filtered.slice().sort((a, b) => (b.year - a.year) || (b.month - a.month)) + }, [world, filter]) + if (!world) return null + + return ( +
+
+ 族史一卷:由系统自动记下的家族大事。倒序排列,可回看任意一年的兴衰细节。 +
+
+ + {Object.entries(CAT_NAME).map(([k, v]) => ( + + ))} +
+
+ {entries.map((e) => ( +
+ {e.year}年{e.month}月 + + {CAT_NAME[e.category] ?? e.category} + {e.text} + +
+ ))} + {entries.length === 0 &&
暂无记载。
} +
+
+ ) +} diff --git a/src/renderer/ui/panels/DiplomacyPanel.tsx b/src/renderer/ui/panels/DiplomacyPanel.tsx new file mode 100644 index 0000000..f1add6b --- /dev/null +++ b/src/renderer/ui/panels/DiplomacyPanel.tsx @@ -0,0 +1,80 @@ +import { useGameStore } from '../store' +import { npcById } from '../../game/data/npcs' +import { giftNpc, makePeace, marryNpcFamily } from '../../game/engine/systems/diplomacy' +import { useMemo } from 'react' +import { MAJOR_NAMES } from '../../game/data/realms' + +const REL_TYPE = (r: number) => + r >= 80 ? ['死生之交', 'good'] : r >= 50 ? ['通好之势', 'good'] : r >= 25 ? ['友善', 'warn'] : r >= 10 ? ['温和', 'warn'] : r > -10 ? ['中立', ''] : r > -30 ? ['嫌隙', ''] : r > -50 ? ['敌对', 'bad'] : ['仇雠', 'bad'] + +export default function DiplomacyPanel() { + const world = useGameStore((s) => s.world) + const bump = useGameStore((s) => s.bump) + const revision = useGameStore((s) => s.revision) + void revision + if (!world) return null + const w = world + const npcs = Object.values(w.state.npcFamilies) + + const sorted = useMemo(() => npcs, [npcs]) + + return ( +
+
+ 外交之道:送礼结好、联姻通好;仇恨过深的老对家会遣队劫掠。家族声望同样影响诸家态度。 +
+ {sorted.map((npc) => { + const def = npcById(npc.id) + const [relLabel, relCls] = REL_TYPE(npc.relation) + const married = npc.allied + return ( +
+
+
{npc.name}
+
{def.style} · {npc.region}
+
+
+
= 0 ? '#2e5a2e' : '#6e2f2f' }}> +
= 0 ? '#5a8a44' : '#8a4444' + }} + /> +
+
+ 关系{npc.relation} · {relLabel} · 势力{npc.power} +
+
+
+ {MAJOR_NAMES[def.leaderRealm]}宗主 +
+
+ + + + {npc.relation < -30 && ( + + )} +
+
+ ) + })} +
+ 示好请用灵石铺路;寻衅会立刻恶化关系,且可能招致劫掠或围庄。 +
+
+ ) +} diff --git a/src/renderer/ui/panels/ExpeditionPanel.tsx b/src/renderer/ui/panels/ExpeditionPanel.tsx new file mode 100644 index 0000000..696b95b --- /dev/null +++ b/src/renderer/ui/panels/ExpeditionPanel.tsx @@ -0,0 +1,132 @@ +import { useGameStore } from '../store' +import { MISSIONS, missionById } from '../../game/data/secrets' +import { sendMission, recallAll } from '../../game/engine/systems/missions' +import { combatPowerOf } from '../../game/engine/systems/combat' +import { useState } from 'react' +import { describeRealm, MAJOR_ORDER } from '../../game/data/realms' + +export default function ExpeditionPanel() { + const world = useGameStore((s) => s.world) + const bump = useGameStore((s) => s.bump) + const selectedMissionDef = useGameStore((s) => s.selectedMissionDef) + const setMissionDef = useGameStore((s) => s.setMissionDef) + const revision = useGameStore((s) => s.revision) + void revision + const [squad, setSquad] = useState([]) + if (!world) return null + const w = world + const s = w.state + const active = s.missions.filter((m) => !m.done) + const candidates = Object.values(s.members).filter( + (c) => c.alive && w.ageOf(c) >= 16 && c.state !== 'expedition' + ) + const def = selectedMissionDef ? missionById(selectedMissionDef) : null + + const toggle = (id: string) => { + setSquad((old) => (old.includes(id) ? old.filter((x) => x !== id) : [...old, id])) + } + + return ( +
+
+ 探秘即遣队伍远行:越远越险,收获越丰。途中遇袭会结算战报——带足好手再上路。 + (同期至多三支队伍在外) +
+
+
择一行路
+
+ {MISSIONS.map((m) => { + const sel = selectedMissionDef === m.id + return ( +
{ + setMissionDef(sel ? undefined : m.id) + setSquad([]) + }} + > +
{m.icon} {m.name}
+
{m.region} · 适合{describeRealm({ major: m.realmHint, minor: 0 })}
+
{m.minMembers}-{m.maxMembers}人 · {m.stages.length}段
+
{m.desc}
+
+ ) + })} +
+ + {def && ( +
+
+ 队伍人选({squad.length}/{def.maxMembers} 人,至少{def.minMembers}人) +
+
+ {candidates.map((c) => ( +
toggle(c.id)} + > + {c.name}·{describeRealm(c.realm)}·战力{Math.round(combatPowerOf(w, c))} +
+ ))} + {candidates.length === 0 && 无可派遣之人} +
+ +
+ )} +
+ + {active.length > 0 && ( +
+
在外队伍
+ {active.map((m) => { + const def2 = missionById(m.defId) + const stage = def2.stages[m.stage] + return ( +
+
+ {def2.icon} {def2.name} + + {m.memberIds.map((id) => s.members[id]?.name ?? '?').join('、')} + + + 第{m.stage + 1}/{def2.stages.length}段 + {stage ? ` · ${stage.title}` : ''} + +
+
+ 启程 {m.startYear}年{m.startMonth}月 · 途中札记: +
+
+ {m.log.slice(-3).map((l, i) => ( +
· {l}
+ ))} +
+ +
+ ) + })} +
+ )} +
+ ) +} diff --git a/src/renderer/ui/panels/FamilyPanel.tsx b/src/renderer/ui/panels/FamilyPanel.tsx new file mode 100644 index 0000000..6924c58 --- /dev/null +++ b/src/renderer/ui/panels/FamilyPanel.tsx @@ -0,0 +1,52 @@ +import { useGameStore } from '../store' +import { MemberCard } from '../components/MemberCard' +import { MemberModal } from '../components/MemberModal' +import { Character } from '../../game/types/domain' + +export default function FamilyPanel() { + const world = useGameStore((s) => s.world) + const selectedMemberId = useGameStore((s) => s.selectedMemberId) + const revision = useGameStore((s) => s.revision) + void revision + if (!world) return null + const s = world.state + const alive = Object.values(s.members).filter((c) => c.alive) + const dead = Object.values(s.members).filter((c) => !c.alive) + const sorted = [...alive].sort((a, b) => { + const aHead = a.id === s.family.headId ? -1 : 0 + const bHead = b.id === s.family.headId ? -1 : 0 + return aHead - bHead || world.ageOf(b) - world.ageOf(a) + }) + const selected = selectedMemberId ? s.members[selectedMemberId] as Character | undefined : undefined + + return ( +
+
+
+ {s.family.name}{' '} + {s.family.estate} +
+
+ 在世人丁 {alive.length} + 家族声望 {s.family.reputation} + 当前代数 {s.family.generation} +
+
+
+ {sorted.map((c) => ( + + ))} + {dead.map((c) => ( + + ))} +
+ {alive.length === 0 && ( +
+
灯下无亲
+
所有族人辞世,家族后继无根。
+
+ )} + {selected && } +
+ ) +} diff --git a/src/renderer/ui/panels/MarketPanel.tsx b/src/renderer/ui/panels/MarketPanel.tsx new file mode 100644 index 0000000..c398f12 --- /dev/null +++ b/src/renderer/ui/panels/MarketPanel.tsx @@ -0,0 +1,170 @@ +import { useGameStore } from '../store' +import { ITEMS } from '../../game/data/items' +import { TECHNIQUES } from '../../game/data/techniques' +import { TECHNIQUE_GRADE_NAMES } from '../../game/data/realms' +import { marketPrice, buyItem, sellItem, buyTechnique } from '../../game/engine/market' +import { useMemo, useState } from 'react' + +export default function MarketPanel() { + const world = useGameStore((s) => s.world) + const bump = useGameStore((s) => s.bump) + const revision = useGameStore((s) => s.revision) + void revision + if (!world) return null + const w = world + const fam = w.state.family + const inv = fam.inventory + const [tab, setTab] = useState<'goods' | 'tech'>('goods') + + const goods = useMemo(() => Object.values(ITEMS).filter((i) => i.kind === 'resource' || i.kind === 'pill'), []) + const artifacts = useMemo(() => Object.values(ITEMS).filter((i) => i.kind === 'artifact'), []) + const marketTech = TECHNIQUES.filter((t) => t.grade <= 3) + const cangshuLv = fam.buildings['cangshu'] ?? 0 + + const price = (id: string) => marketPrice(w, id) + + return ( +
+
+
setTab('goods')}>货栈
+
setTab('tech')}>法帖
+
+ {tab === 'goods' && ( + <> +
+ 集市物价随季节波动(当前行情 {Math.round(((fam.flag['priceMult'] as number) ?? 1) * 100)}%)。 + 仓库寄存的灵草、灵矿、兽核均可在此出手。 +
+ + + + + + + + + + + + {[...goods, ...artifacts].map((item) => { + const have = item.id === 'lingcao' || item.id === 'lingkuang' || item.id === 'beastcore' || item.kind === 'pill' || item.kind === 'artifact' + ? inv[item.id] ?? 0 + : 0 + const stock = item.kind === 'artifact' ? 3 : item.kind === 'pill' ? 5 : 9999 + const count = item.kind === 'artifact' ? 1 : 5 + return ( + + + + + + + + ) + })} + + + + + + + + +
品名说明行价库存/持有买卖
+ {item.icon} {item.name} + {item.desc}{price(item.id)} 灵石{have} + {' '} + {((item.kind === 'resource' || item.kind === 'pill') && have >= count) && ( + + )} + {item.kind === 'artifact' && have > 0 && ( + 已购藏 + )} +
丹房炼制灵草+辅矿 → 丹药(丹房等级越高成数越足){fam.buildings['danfang'] ? `${fam.buildings['danfang']}级丹房` : '未建丹房'}灵草{inv['lingcao'] ?? 0} + {fam.buildings['danfang'] && ( + <> + {' '} + + + )} +
+ + )} + {tab === 'tech' && ( + <> +
+ 藏书阁录存法帖,族人可修习。藏书阁等级决定可在坊市搜购的品阶(当前 {cangshuLv} 级,可购至{TECHNIQUE_GRADE_NAMES[Math.min(3, Math.max(0, cangshuLv))]})。 + 若藏书阁未建或等级不足,只可见基础法帖。 +
+ + + + + + + + + + + + + {marketTech + .filter((t) => t.grade <= 1 + cangshuLv) + .filter((t) => t.grade <= 3) + .map((t) => { + const owned = fam.techniques.includes(t.id) + const p = t.grade <= 1 + cangshuLv + const priceT = [120, 300, 700, 1600, 3600][t.grade] ?? 300 + return ( + + + + + + + + + ) + })} + +
法帖品阶/路子妙用修为加成战力加成
《{t.name}》{TECHNIQUE_GRADE_NAMES[t.grade]} · {t.path}{t.desc}+{Math.round((t.expBonus) * 100)}%+{Math.round((t.powerBonus) * 100)}% + {owned ? ( + 已录 + ) : ( + + )} +
+ + )} +
+ 灵矿不足时可上坊市民间收购;妖丹于探秘与灵兽园中可得。 +
+
+ ) +} diff --git a/src/renderer/ui/panels/SettingsPanel.tsx b/src/renderer/ui/panels/SettingsPanel.tsx new file mode 100644 index 0000000..9db0f61 --- /dev/null +++ b/src/renderer/ui/panels/SettingsPanel.tsx @@ -0,0 +1,68 @@ +import { useGameStore } from '../store' + +export default function SettingsPanel() { + const world = useGameStore((s) => s.world) + const bump = useGameStore((s) => s.bump) + const slot = useGameStore((s) => s.slot) + const saveNow = useGameStore((s) => s.saveNow) + const exportSave = useGameStore((s) => s.exportSave) + const importSave = useGameStore((s) => s.importSave) + const refreshSlots = useGameStore((s) => s.refreshSlots) + const go = useGameStore((s) => s.go) + const setSpeed = useGameStore((s) => s.setSpeed) + if (!world) return null + + return ( +
+
存档与导出
+
+ 手动存点(当前第 {slot} 档) + +
+
+ 导出存档(JSON 文件) + +
+
+ 导入存档到当前位置 + +
+
+ 刷新存档槽 + +
+
+ 暂停时间 + +
+
+ 回到主菜单(会先自动保存) + +
+
+ 立即推送修复(保存完成提示) + +
+ +
玩法说明
+
+ · 日常以「月」流转:成员修炼、建筑产出、往事兴替、事件沓来。
+ · 「宗」字牌成员可闭关、服丹、冲击瓶颈;瓶颈即修为满百者,成功率看灵气、丹药与家主督学。
+ · 「探秘」选人结队远行,途中见战报;凯旋带回灵石、法帖、法器诸宝。
+ · 「外交」与四邻结好联姻;仇雠之族隔岁来犯,打得赢名望大涨,打不赢蚀钱伤丁。
+ · 「史书」自动记述繁华与凋零——百年之后,后人翻开这一卷家族志,见代代薪火、历历雪泥。 +
+
版本 0.1.0 · Chronicle of the Immortal Clan
+
+ ) +} diff --git a/src/renderer/ui/panels/TerritoryPanel.tsx b/src/renderer/ui/panels/TerritoryPanel.tsx new file mode 100644 index 0000000..f4729f4 --- /dev/null +++ b/src/renderer/ui/panels/TerritoryPanel.tsx @@ -0,0 +1,102 @@ +import { useGameStore } from '../store' +import { BUILDINGS } from '../../game/data/buildings' +import { useMemo } from 'react' + +export default function TerritoryPanel() { + const world = useGameStore((s) => s.world) + const bump = useGameStore((s) => s.bump) + const revision = useGameStore((s) => s.revision) + void revision + if (!world) return null + const fam = world.state.family + const ids = useMemo(() => Object.keys(BUILDINGS), []) + + const levelLabel = (l: number) => '·'.repeat(l) + '。'.repeat(5 - l) + + return ( +
+
+ 族产经营:建造建筑后每月自动产出;坊市、丹房等基础设施可解锁功能。 + 灵石与灵矿不足则无法建造或升级。 +
+
+ {ids.map((id) => { + const def = BUILDINGS[id] + if (!def) return null + const level = fam.buildings[id] ?? 0 + const built = level > 0 + const cost = built && level >= def.maxLevel ? null : def.upgradeCost(built ? level + 1 : 1) + const produce = def.produceTable ? def.produceTable(built ? level : 1) : null + const canUpgrade = + !!cost && + fam.stones >= cost.stones && + fam.inventory['lingkuang'] >= cost.lingkuang + return ( +
+
+ {def.icon} +
+
{def.name}
+
{def.kind === 'produce' ? '产出' : '功效'}
+
+ {built ? `${level}级` : ''} +
+
{def.desc}
+ {produce && Object.keys(produce).length > 0 && ( +
+ {Object.entries(produce) + .filter(([k]) => k !== 'beastcore' || (built && level >= 3)) + .map(([k, v]) => `${itemLabel(k)} ×${v}`) + .join(' / ')} + /月 +
+ )} +
+ {built ? ( + <> + {level < def.maxLevel ? ( + + ) : ( + 已臻极致 + )} + {levelLabel(level)} + + ) : ( + + )} +
+
+ ) + })} +
+
+ ) +} + +function itemLabel(k: string): string { + const map: Record = { + stone: '灵石', + lingcao: '灵草', + lingkuang: '灵矿', + beastcore: '兽核' + } + return map[k] ?? k +} diff --git a/src/renderer/ui/screens/Boot.tsx b/src/renderer/ui/screens/Boot.tsx new file mode 100644 index 0000000..e40addb --- /dev/null +++ b/src/renderer/ui/screens/Boot.tsx @@ -0,0 +1,71 @@ +import { useGameStore } from '../store' +import { SaveMeta } from '../../game/types/domain' +import mountainUrl from '../../assets/mountain.svg' + +function slotLabel(meta: SaveMeta | null): string { + if (!meta) return '虚位以待' + return `${meta.name} · ${meta.year}年${meta.month}月 · ${meta.members}人 · 声望${meta.reputation}` +} + +export default function Boot() { + const slots = useGameStore((s) => s.slots) + const go = useGameStore((s) => s.go) + const continueGame = useGameStore((s) => s.continueGame) + const deleteSave = useGameStore((s) => s.deleteSave) + const toast = useGameStore((s) => s.toast) + + return ( +
+
+ +
+
+
仙途家族志
+
一 族 兴 衰 · 千 秋 烟 云
+
+
CHRONICLE OF THE IMMORTAL CLAN
+
+ + {slots.map((meta, i) => ( +
+ {meta &&
{i + 1}
} + {meta ? ( + <> +
void continueGame(i + 1)}> +
第{i + 1}档
+
{slotLabel(meta)}
+
+ + + ) : ( +
go('newgame')}> + {i + 1} + 虚位以待 +
+ )} +
+ ))} +
+
+ 癸未年 + + 燃灯续香火 · 卷中写春秋 + +
+
+ {toast &&
{toast}
} +
+ ) +} diff --git a/src/renderer/ui/screens/GameScreen.tsx b/src/renderer/ui/screens/GameScreen.tsx new file mode 100644 index 0000000..0e21f20 --- /dev/null +++ b/src/renderer/ui/screens/GameScreen.tsx @@ -0,0 +1,113 @@ +import { useGameStore } from '../store' +import { PanelId } from '../store' +import { RES_INFO } from '../components' +import FamilyPanel from '../panels/FamilyPanel' +import TerritoryPanel from '../panels/TerritoryPanel' +import MarketPanel from '../panels/MarketPanel' +import DiplomacyPanel from '../panels/DiplomacyPanel' +import ExpeditionPanel from '../panels/ExpeditionPanel' +import ChroniclePanel from '../panels/ChroniclePanel' +import SettingsPanel from '../panels/SettingsPanel' +import { LogFeed } from '../components/LogFeed' + +const TABS: { id: PanelId; label: string }[] = [ + { id: 'family', label: '宗族' }, + { id: 'territory', label: '领地' }, + { id: 'market', label: '坊市' }, + { id: 'diplomacy', label: '外交' }, + { id: 'expedition', label: '探秘' }, + { id: 'chronicle', label: '史书' }, + { id: 'settings', label: '设置' } +] + +export default function GameScreen() { + const world = useGameStore((s) => s.world) + const panel = useGameStore((s) => s.panel) + const setPanel = useGameStore((s) => s.setPanel) + const revision = useGameStore((s) => s.revision) + const speed = useGameStore((s) => s.speed) + const setSpeed = useGameStore((s) => s.setSpeed) + const advance = useGameStore((s) => s.advance) + const saveNow = useGameStore((s) => s.saveNow) + const toast = useGameStore((s) => s.toast) + const gameOverReason = useGameStore((s) => s.gameOverReason) + + void revision + void world + + const st = useGameStore.getState() + const w = st.world + if (!w) return null + const s = w.state + const fam = s.family + + return ( +
+
+
{fam.name}
+
+ {s.year}年{s.month}月 · 第{fam.generation}代 · 声望{fam.reputation} +
+
+ + + + + +
+
+ {RES_INFO.map((r) => ( +
+ {r.icon} + {dispRes(r.id)} +
+ ))} +
+ +
+
+ {TABS.map((t) => ( +
setPanel(t.id)}> + {t.label} +
+ ))} +
+ {fam.motto} +
+
+
+
+ {panel === 'family' && } + {panel === 'territory' && } + {panel === 'market' && } + {panel === 'diplomacy' && } + {panel === 'expedition' && } + {panel === 'chronicle' && } + {panel === 'settings' && } + {gameOverReason && ( +
+

家门不幸

+

{gameOverReason}

+

+ 家族于 {s.year} 年 {s.month} 月戛然而止。可在史书中凭吊这家百年。 +

+
+ )} +
+
+
宗族闻讯
+ +
+
+ {toast &&
{toast}
} +
+ ) +} + +function dispRes(id: string): number { + const st = useGameStore.getState() + const w = st.world + if (!w) return 0 + if (id === 'stones') return w.state.family.stones + return w.state.family.inventory[id] ?? 0 +} diff --git a/src/renderer/ui/screens/NewGame.tsx b/src/renderer/ui/screens/NewGame.tsx new file mode 100644 index 0000000..bde41fc --- /dev/null +++ b/src/renderer/ui/screens/NewGame.tsx @@ -0,0 +1,93 @@ +import { useMemo, useState } from 'react' +import { useGameStore } from '../store' +import { SURNAME_POOL } from '../../game/core/names' + +export default function NewGame() { + const go = useGameStore((s) => s.go) + const startNewGame = useGameStore((s) => s.startNewGame) + const [surname, setSurname] = useState('林') + const [familyName, setFamilyName] = useState('林家') + const [motto, setMotto] = useState('耕读传家,术法继世') + const [difficulty, setDifficulty] = useState<'easy' | 'normal' | 'hard'>('normal') + const [slot, setSlot] = useState(1) + const [randoming, setRandoming] = useState(false) + + const seed = useMemo( + () => `${Date.now()}-${Math.floor(Math.random() * 1e9)}-${Math.floor(Math.random() * 1e9)}`, + [randoming] + ) + + const rollNames = () => { + setRandoming((r) => !r) + const s = SURNAME_POOL[Math.floor(Math.random() * SURNAME_POOL.length)] + setSurname(s) + setFamilyName(`${s}氏`) + } + + return ( +
+
+
+ — 创建家族 — +
+
+ + setSurname(e.target.value)} maxLength={2} /> + +
+
+ + setFamilyName(e.target.value)} maxLength={10} placeholder="例如:林氏" /> +
+
+ + setMotto(e.target.value)} maxLength={20} /> +
+
+ +
+ {( + [ + ['easy', '顺境', '灵石丰足,时来运转'], + ['normal', '中庸', '中规中矩,全靠经营'], + ['hard', '逆境', '部曲拮据,波折不断'] + ] as const + ).map(([k, label, desc]) => ( +
setDifficulty(k)} + > +
{label}
+
{desc}
+
+ ))} +
+
+
+ + +
+
+ + +
+
+ 你将扮演家主,经营一族兴衰:修炼突破、内外经营、结交应敌,直至百年之后。 +
+
+
+ ) +} diff --git a/src/renderer/ui/store.ts b/src/renderer/ui/store.ts new file mode 100644 index 0000000..1c2ce8a --- /dev/null +++ b/src/renderer/ui/store.ts @@ -0,0 +1,292 @@ +import { create } from 'zustand' +import { World, WorldEventBus } from '../game/engine/world' +import { EventDef } from '../game/data/events' +import { findEvent, applyEventChoice } from '../game/engine/systems/events' +import { BattleLog, ChronicleEntry, GameState, LogItem, SaveMeta } from '../game/types/domain' +import { getSlotManager, getSaveSlot } from '../game/storage/db' +import { metaFromState, updateSlotMeta } from './storeHelper' + +export type Screen = 'boot' | 'newgame' | 'game' +export type PanelId = 'family' | 'territory' | 'market' | 'diplomacy' | 'expedition' | 'chronicle' | 'settings' + +export interface GameStore { + screen: Screen + world: World | null + slot: number + slots: (SaveMeta | null)[] + speed: number + timer: ReturnType | null + logFeed: LogItem[] + pendingEventId?: string + pendingEventDef?: EventDef + battleView?: BattleLog + panel: PanelId + selectedMemberId?: string + selectedMissionDef?: string + revision: number + toast?: string + gameOverReason?: string + + init: () => Promise + go: (screen: Screen) => void + startNewGame: (opts: { seed: string; surname: string; familyName: string; motto: string; difficulty: 'easy' | 'normal' | 'hard' }, slot: number) => Promise + continueGame: (slot: number) => Promise + loadSnapshot: (slot: number, snapshotId: string) => Promise + advance: () => Promise + addLog: (item: LogItem) => void + onChronicle: (e: ChronicleEntry, important: boolean) => void + onBattle: (log: BattleLog) => void + onPendingEvent: (id: string) => void + onGameOver: (reason: string) => void + saveNow: (label?: string) => Promise + bump: () => void + setSpeed: (v: number) => void + setPanel: (p: PanelId) => void + setSelected: (id?: string) => void + setMissionDef: (id?: string) => void + exportSave: () => Promise + importSave: (slot: number) => Promise + deleteSave: (slot: number) => Promise + refreshSlots: () => Promise +} + +const LOG_CAP = 260 +let logSeq = 1 + +export const useGameStore = create((set, get) => ({ + screen: 'boot', + world: null, + slot: 1, + slots: [], + speed: 0, + timer: null, + logFeed: [], + pendingEventId: undefined, + pendingEventDef: undefined, + battleView: undefined, + panel: 'family', + selectedMemberId: undefined, + selectedMissionDef: undefined, + revision: 0, + toast: undefined, + gameOverReason: undefined, + + init: async () => { + const manager = getSlotManager() + const slots = await manager.listSlotMetas() + set({ slots }) + if (typeof window !== 'undefined') { + ;(window as unknown as Record).__cotycBootReady = true + const hack = window as unknown as Record + hack.__cotycDebug = { + startNewGame: () => get().startNewGame({ seed: 'smoke-' + Date.now(), surname: '林', familyName: '林氏', motto: 'm', difficulty: 'normal' }, 1), + advance: () => get().advance(), + resolvePending: () => { + const st = get() + if (st.pendingEventId && st.world) { + applyEventChoice(st.world, st.pendingEventId, 0) + useGameStore.setState({ pendingEventId: undefined, pendingEventDef: undefined }) + st.bump() + } + }, + getYear: () => get().world?.state.year ?? 0, + openPanel: (p: string) => { + useGameStore.setState({ panel: p as never }) + }, + closeBattles: () => { + useGameStore.setState({ battleView: undefined }) + }, + getChronicle: () => get().world?.state.chronicle?.slice().reverse() ?? [], + getFeed: () => get().logFeed.slice() + } + } + }, + + go: (screen) => set({ screen }), + + startNewGame: async (opts, slot) => { + const world = World.create(opts) + set({ world, slot, screen: 'game', panel: 'family', logFeed: [], battleView: undefined, pendingEventId: undefined, pendingEventDef: undefined, revision: 1, speed: 0 }) + const st = get() + st.world?.out.push(makeBus(st)) + await st.saveNow('开局') + }, + + continueGame: async (slot) => { + const manager = getSlotManager() + const file = await getSaveSlot(slot) + await file.open() + const state = await file.loadState() + if (!state) throw new Error('找不到存档数据') + openState(state, slot) + await manager.updateSlotMeta(slot, metaFromState(state, slot)) + }, + + loadSnapshot: async (slot, snapshotId) => { + const file = await getSaveSlot(slot) + await file.open() + const state = await file.loadState(snapshotId) + if (!state) throw new Error('找不到快照') + openState(state, slot) + }, + + advance: async () => { + const st = get() + const w = st.world + if (!w || st.pendingEventId || w.state.gameOver) return + try { + w.advanceMonth() + w.syncRng() + set((s) => ({ revision: s.revision + 1 })) + await Stash.save(st, '自动') + } catch (e) { + console.error(e) + } + }, + + addLog: (item) => { + set((s) => { + const next = (s.logFeed.length >= LOG_CAP ? s.logFeed.slice(s.logFeed.length - LOG_CAP + 1) : s.logFeed).concat(item) + return { logFeed: next } + }) + }, + + onChronicle: (e, important) => { + if (important) { + set((s) => ({ revision: s.revision + 1 })) + } + }, + + onBattle: (log) => set({ battleView: log, revision: get().revision + 1 }), + + onPendingEvent: (id) => { + const def = findEvent(id) + set({ pendingEventId: id, pendingEventDef: def }) + }, + + onGameOver: (reason) => set({ gameOverReason: reason, speed: 0, revision: get().revision + 1 }), + + saveNow: async (label = '手动') => { + await Stash.save(get(), label) + }, + + bump: () => set((s) => ({ revision: s.revision + 1 })), + + setSpeed: (v) => { + const st = get() + if (st.timer) clearInterval(st.timer) + if (v <= 0) { + set({ speed: v, timer: null }) + return + } + const ms = v === 1 ? 3200 : v === 2 ? 1800 : 900 + if (v >= 3) v = 3 + const timer = setInterval(() => { + const cur = get() + if (!cur.pendingEventId && cur.world && !cur.world.state.gameOver) { + void cur.advance() + } + }, ms) + set({ speed: v, timer }) + }, + + setPanel: (p) => set({ panel: p }), + setSelected: (id) => set({ selectedMemberId: id }), + setMissionDef: (id) => set({ selectedMissionDef: id }), + + exportSave: async () => { + const st = get() + if (!st.world || !window.api) return + const file = await getSaveSlot(st.slot) + const meta0 = metaFromState(st.world.state, st.slot) + st.world.syncRng() + await file.saveState(st.world.state, '导出前') + const json = await file.exportAll() + const meta = metaFromState(st.world.state, st.slot) + const r = await window.api.exportSave(json, `${meta.name}-年${meta.year}年`) + if (r.ok) set({ toast: '存档已导出。' }) + else set({ toast: `导出失败:${r.error ?? ''}` }) + }, + + importSave: async (slot) => { + if (!window.api) return + const r = await window.api.importSave() + if (!r.ok || !r.text) { + set({ toast: `导入失败:${r.error ?? ''}` }) + return + } + const file = await getSaveSlot(slot) + await file.open() + const ok = await file.importAll(r.text) + if (ok) { + set({ toast: '导入成功,重新读取存档槽。' }) + await get().refreshSlots() + } else { + set({ toast: '导入失败:文件格式不正确。' }) + } + }, + + deleteSave: async (slot) => { + const file = await getSaveSlot(slot) + await file.open() + await file.wipe() + const manager = getSlotManager() + await manager.removeSlotMeta(slot) + await get().refreshSlots() + }, + + refreshSlots: async () => { + const manager = getSlotManager() + const slots = await manager.listSlotMetas() + set({ slots }) + } +})) + +function openState(state: GameState, slot: number): void { + const world = new World(state) + const st = useGameStore.getState() + world.out.push(makeBus(st)) + useGameStore.setState({ + world, + slot, + screen: 'game', + panel: 'family', + logFeed: [], + battleView: undefined, + pendingEventId: undefined, + pendingEventDef: undefined, + revision: 1, + speed: 0, + gameOverReason: state.gameOver?.reason + }) +} + +function makeBus(st: GameStore): WorldEventBus { + return { + onLog: (kind, text) => { + const w = st.world + st.addLog({ id: logSeq++, kind, text, year: w?.state.year ?? 0, month: w?.state.month ?? 0 }) + }, + onChronicle: (e, important) => { + st.addLog({ id: logSeq++, kind: 'chronicle', text: `${e.year}年${e.month}月 ${e.text}`, year: e.year, month: e.month }) + if (important) st.onChronicle(e, important) + }, + onBattle: (log) => st.onBattle(log), + onPendingEvent: (id) => st.onPendingEvent(id), + onGameOver: (reason) => st.onGameOver(reason) + } +} + +const Stash = { + async save(st: GameStore, label: string): Promise { + if (!st.world) return + const file = await getSaveSlot(st.slot) + await file.open() + st.world.syncRng() + await file.saveState(st.world.state, label) + await file.saveChronicle(st.world.state.chronicle) + const meta = metaFromState(st.world.state, st.slot) + await file.setMeta(meta) + await updateSlotMeta(st.slot, meta) + } +} diff --git a/src/renderer/ui/storeHelper.ts b/src/renderer/ui/storeHelper.ts new file mode 100644 index 0000000..4681f7d --- /dev/null +++ b/src/renderer/ui/storeHelper.ts @@ -0,0 +1,24 @@ +import { GameState, SaveMeta } from '../game/types/domain' +import { getSlotManager } from '../game/storage/db' + +export function metaFromState(state: GameState, slot: number): SaveMeta { + const alive = Object.values(state.members).filter((c) => c.alive).length + return { + slot, + surname: state.family.surname, + name: state.family.name, + estate: state.family.estate, + year: state.year, + month: state.month, + generation: state.family.generation, + members: alive, + reputation: state.family.reputation, + updatedAt: new Date().toISOString(), + version: state.schemaVersion + } +} + +export async function updateSlotMeta(slot: number, meta: SaveMeta): Promise { + const manager = getSlotManager() + await manager.updateSlotMeta(slot, meta) +} diff --git a/src/renderer/ui/styles.css b/src/renderer/ui/styles.css new file mode 100644 index 0000000..3bad1d2 --- /dev/null +++ b/src/renderer/ui/styles.css @@ -0,0 +1,1273 @@ +/* ============================================================ + 仙途家族志 · 水墨中国风主题 + 墨为底、纸为面、朱砂点睛、泥金饰边 + ============================================================ */ + +:root { + --ink-bg: #100d0a; + --ink-bg2: #16120d; + --ink-deep: #0b0906; + --paper: #f2e9d4; + --paper-dim: #e3d6b8; + --paper-dark: #cdbfa0; + --ink: #2a2214; + --ink-soft: #554a35; + --ink-faint: #8b7f66; + --vermilion: #a93b2e; + --vermilion-soft: #c05a41; + --vermilion-bright: #d06a4c; + --gold: #b3913e; + --gold-soft: #c7a44b; + --green: #56704d; + --blue-ink: #4d616e; + font-size: 15px; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, +body, +#root { + height: 100%; +} + +body { + background: var(--ink-bg); + color: var(--paper); + font-family: "STKaiti", "KaiTi", "Kaiti SC", "华文楷体", "楷体", "Noto Serif SC", "Source Han Serif SC", serif; + overflow: hidden; + -webkit-font-smoothing: antialiased; +} + +.app { + height: 100vh; + display: flex; + flex-direction: column; +} + +/* ---------- 纸纹 overlay ---------- */ +.paper-tex { + background-image: url("data:image/svg+xml;utf8,"); + background-blend-mode: multiply; + background-size: 160px 160px; +} + +::-webkit-scrollbar { + width: 9px; + height: 9px; +} +::-webkit-scrollbar-thumb { + background: linear-gradient(180deg, #3d3527, #574e3a); + border-radius: 6px; + border: 2px solid rgba(0, 0, 0, 0.4); +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-corner { + background: transparent; +} + +::selection { + background: rgba(169, 59, 46, 0.45); + color: #fdf6e3; +} + +/* ---------- 通用 ---------- */ +.btn { + background: linear-gradient(180deg, #20180f, #191209); + border: 1px solid #3d3221; + color: #d8c8a8; + padding: 6px 14px; + border-radius: 3px; + cursor: pointer; + font-family: inherit; + font-size: 0.95rem; + letter-spacing: 1px; + transition: all 0.16s ease; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 236, 190, 0.05); +} +.btn:hover:not(:disabled) { + border-color: var(--gold); + color: var(--gold-soft); + box-shadow: 0 0 14px rgba(179, 145, 62, 0.25), 0 2px 6px rgba(0, 0, 0, 0.45); +} +.btn:active:not(:disabled) { + transform: translateY(1px); +} +.btn:disabled { + opacity: 0.34; + cursor: not-allowed; +} +.btn-primary { + background: linear-gradient(180deg, #b54739, #8e2f24); + border-color: #d06a4c; + color: #fef3dc; + text-shadow: 0 1px 2px rgba(60, 12, 6, 0.5); +} +.btn-primary:hover:not(:disabled) { + border-color: var(--gold-soft); + box-shadow: 0 0 18px rgba(192, 90, 65, 0.5); +} +.btn-danger { + border-color: #6b3228; + color: #d08b7a; +} +.btn-sm { + padding: 2px 9px; + font-size: 0.85rem; + border-radius: 2px; +} +.btn-lg { + padding: 10px 34px; + font-size: 1.12rem; + letter-spacing: 4px; +} + +/* ---------- 宣纸卡片 ---------- */ +.card { + background: + linear-gradient(178deg, rgba(255, 250, 232, 0.5), rgba(210, 190, 150, 0.25)), + linear-gradient(180deg, #f2e9d4, #e8d8b8); + background-color: var(--paper); + color: var(--ink); + border: 1px solid #b7a379; + border-radius: 3px; + padding: 12px 14px; + box-shadow: + 0 3px 14px rgba(0, 0, 0, 0.5), + inset 0 0 0 1px rgba(255, 250, 235, 0.55), + inset 0 0 40px rgba(122, 98, 52, 0.12); + background-image: + url("data:image/svg+xml;utf8,"), + linear-gradient(175deg, rgba(255, 251, 236, 0.72), var(--paper-dim)); +} + +.card-title { + color: #64451e; + font-size: 1.06rem; + margin-bottom: 8px; + letter-spacing: 3px; + position: relative; + padding-bottom: 6px; +} +.card-title::after { + content: ""; + position: absolute; + left: 0; + bottom: 0; + width: 52px; + height: 2px; + border-radius: 2px; + background: linear-gradient(90deg, var(--vermilion), rgba(169, 59, 46, 0.05)); +} + +.dim { + color: #6d6150; + font-size: 0.9rem; +} +.dim2 { + color: #96896e; + font-size: 0.85rem; +} +.good { + color: #4e6b42; +} +.bad { + color: #a13b2c; +} +.warn { + color: #a06f18; +} +.gold { + color: #8a6416; +} + +/* ---------- 印章式图标 ---------- */ +.s-icon, +.res-icon, +.bld-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border-radius: 4px; + background: linear-gradient(180deg, #453012, #271a08); + border: 1px solid #8a6d2f; + color: #f2ddb0; + font-size: 0.85rem; + vertical-align: middle; + flex-shrink: 0; + user-select: none; + box-shadow: + inset 0 1px 0 rgba(255, 240, 200, 0.16), + 0 2px 5px rgba(0, 0, 0, 0.45); + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.6); +} +.res-icon { + width: 18px; + height: 18px; + font-size: 0.68rem; + border-radius: 3px; + background: linear-gradient(180deg, #38270f, #1e1507); +} +.bld-icon { + width: 32px; + height: 32px; + font-size: 1.08rem; + border-radius: 5px; + background: linear-gradient(180deg, #4d3a20, #2b1d0c); + color: #f4dda5; +} + +/* ---------- 状态条(墨染) ---------- */ +.bar { + height: 7px; + background: rgba(35, 27, 14, 0.75); + border: 1px solid rgba(76, 62, 35, 0.6); + border-radius: 5px; + overflow: hidden; + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.55); +} +.bar > div { + height: 100%; + background: linear-gradient(90deg, #6d7a54, #86b5a2 60%, #b8d9c4); + border-radius: 4px; + transition: width 0.35s ease; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35); +} +.bar.bar-red > div { + background: linear-gradient(90deg, #8e2f24, #c05a41 60%, #e08a6d); +} + +/* ---------- 走笔分隔线 ---------- */ +.brush-line { + height: 3px; + border-radius: 3px; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(179, 145, 62, 0.55) 12%, + rgba(179, 145, 62, 0.25) 30%, + rgba(179, 145, 62, 0.6) 46%, + rgba(169, 59, 46, 0.42) 62%, + rgba(179, 145, 62, 0.25) 80%, + transparent 100% + ); + margin: 10px 0; +} + +/* ---------- 印章 ---------- */ +.seal { + display: inline-flex; + align-items: center; + justify-content: center; + background: var(--vermilion); + color: #f8efd9; + font-family: "STKaiti", "KaiTi", serif; + border-radius: 3px; + box-shadow: + 0 2px 6px rgba(0, 0, 0, 0.6), + inset 0 0 6px rgba(255, 220, 200, 0.35); + padding: 2px 5px; + font-size: 0.72rem; + letter-spacing: 1px; + transform: rotate(-2deg); +} + +/* ============================================================ + BOOT · 谷口研墨,远山入卷 + ============================================================ */ +.boot { + position: relative; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 30px; + overflow: hidden; + background: + radial-gradient(ellipse at 50% 8%, #35281a 0%, #181209 48%, #0e0b07 100%); +} +.boot-bg { + position: absolute; + inset: 0; + opacity: 0.9; + pointer-events: none; +} +.boot-bg::after { + content: ""; + position: absolute; + inset: 0; + background: + radial-gradient(ellipse at 50% 20%, rgba(0, 0, 0, 0) 0%, rgba(10, 8, 5, 0.55) 75%), + url("data:image/svg+xml;utf8,"); +} +.boot-bg img { + width: 100%; + height: 100%; + object-fit: cover; + opacity: 0.95; + filter: saturate(1.12) brightness(1.15) contrast(1.06); +} + +.boot-inner { + position: relative; + z-index: 2; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; +} + +.boot-title { + font-size: 4.6rem; + color: #f3e9cd; + letter-spacing: 22px; + text-indent: 22px; + line-height: 1.1; + font-weight: 700; + text-shadow: + 0 2px 0 rgba(20, 12, 4, 0.85), + 0 6px 24px rgba(0, 0, 0, 0.9), + 0 0 60px rgba(220, 170, 90, 0.18); +} +.boot-tagline { + letter-spacing: 13px; + color: #c8ac72; + font-size: 0.95rem; + text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8); +} +.boot-en { + letter-spacing: 5px; + color: #7d6c4a; + font-size: 0.72rem; +} +.boot-divider { + width: 380px; + height: 2px; + margin-top: 6px; + background: linear-gradient(90deg, transparent, rgba(240, 220, 170, 0.5), transparent); + position: relative; +} +.boot-divider::after { + content: "◆"; + position: absolute; + left: 50%; + top: -0.62em; + transform: translateX(-50%); + color: var(--vermilion-soft); + font-size: 0.8rem; + text-shadow: 0 0 8px rgba(192, 90, 65, 0.8); +} +.boot-actions { + display: flex; + flex-direction: column; + gap: 11px; + width: 330px; +} +.boot-newgame { + padding: 13px 0; + font-size: 1.18rem; + letter-spacing: 10px; + background: linear-gradient(180deg, #b54739, #8e2f24); + border: 1px solid #d98a66; + color: #fdf3da; + border-radius: 3px; + cursor: pointer; + font-family: inherit; + text-shadow: 0 1px 2px rgba(70, 15, 8, 0.65); + box-shadow: 0 8px 26px rgba(140, 40, 24, 0.35), inset 0 1px 0 rgba(255, 240, 210, 0.28); + transition: all 0.18s; +} +.boot-newgame:hover { + box-shadow: 0 10px 32px rgba(180, 60, 35, 0.5), inset 0 1px 0 rgba(255, 240, 210, 0.3); + border-color: var(--gold-soft); +} + +.boot-slot { + display: flex; + align-items: center; + gap: 12px; + background: + url("data:image/svg+xml;utf8,"), + linear-gradient(180deg, #241b11 0%, #1c140c 100%); + border: 1px solid #3a3021; + border-radius: 3px; + padding: 10px 14px; + cursor: pointer; + transition: all 0.18s; + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5); +} +.boot-slot:hover { + border-color: var(--gold); + transform: translateY(-1px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.6); +} +.boot-slot .slot-no { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #4d3f28; + border-radius: 50%; + color: var(--gold-soft); + font-size: 0.85rem; + flex-shrink: 0; +} +.boot-slot-empty { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + background: rgba(12, 9, 5, 0.45); + border: 1px dashed #3a3021; + min-height: 52px; +} + +/* ============================================================ + 新游戏 + ============================================================ */ +.newgame { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background: + radial-gradient(ellipse at 60% 30%, #261c10, #100c08 85%); +} +.newgame-box { + width: 560px; + display: flex; + flex-direction: column; + gap: 16px; + padding: 26px 30px; +} +.newgame .card-title { + font-size: 1.6rem; + color: #d9bd85; + letter-spacing: 10px; +} +.field { + display: flex; + align-items: center; + gap: 12px; +} +.field label { + width: 80px; + color: #b09f7c; + letter-spacing: 2px; +} +.field input, +.field select { + flex: 1; + background: rgba(14, 11, 7, 0.8); + border: 1px solid #3a3021; + color: #ecdcb8; + padding: 8px 10px; + border-radius: 3px; + font-family: inherit; + font-size: 1rem; + outline: none; +} +.field input:focus, +.field select:focus { + border-color: var(--gold); + box-shadow: 0 0 10px rgba(179, 145, 62, 0.25); +} +.diff-grid { + display: flex; + gap: 8px; +} +.diff-opt { + flex: 1; + text-align: center; + padding: 10px; + background: rgba(20, 15, 9, 0.65); + border: 1px solid #3a3021; + border-radius: 3px; + cursor: pointer; + color: #cbbd9e; + transition: all 0.16s; +} +.diff-opt:hover { + border-color: #6b5836; +} +.diff-opt.sel { + border-color: var(--vermilion-soft); + color: #f3e6c8; + background: linear-gradient(180deg, #2b1d12, #1f150c); + box-shadow: 0 0 12px rgba(169, 59, 46, 0.3); +} + +/* ============================================================ + TOPBAR · 檐下横匾 + ============================================================ */ +.topbar { + display: flex; + align-items: center; + gap: 14px; + padding: 9px 18px; + background: linear-gradient(180deg, #1d1710 0%, #141009 100%); + border-bottom: 1px solid #3a3021; + flex-shrink: 0; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.55); + position: relative; +} +.topbar::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: -3px; + height: 3px; + background: linear-gradient( + 90deg, + transparent, + rgba(179, 145, 62, 0.35) 20%, + transparent 50%, + rgba(169, 59, 46, 0.28) 78%, + transparent + ); + pointer-events: none; +} +.topbar .fam-name { + font-size: 1.34rem; + color: #dfc283; + letter-spacing: 6px; + font-weight: 700; + text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9); + white-space: nowrap; +} +.topbar .date { + white-space: nowrap; + color: #a08f6b; + font-size: 0.98rem; + letter-spacing: 1px; + background: rgba(38, 29, 16, 0.65); + border: 1px solid #33291a; + border-radius: 2px; + padding: 3px 10px; +} +.speed-ctl { + display: flex; + align-items: center; + gap: 1px; +} +.speed-btn { + width: 30px; + height: 30px; + background: rgba(10, 8, 5, 0.6); + border: 1px solid #33291a; + color: #8f8264; + cursor: pointer; + font-family: inherit; + font-size: 0.85rem; + transition: all 0.15s; +} +.speed-btn:hover { + color: #d8c392; + border-color: #5b4a2e; +} +.speed-btn.sel { + background: linear-gradient(180deg, #4a3620, #352718); + border-color: var(--gold); + color: var(--gold-soft); + box-shadow: inset 0 0 8px rgba(179, 145, 62, 0.3); +} + +.ress { + display: flex; + gap: 12px; + align-items: center; + margin-left: auto; + font-size: 0.95rem; + white-space: nowrap; +} +.res-item { + display: flex; + align-items: center; + gap: 5px; + color: #cbbd9e; + background: rgba(38, 29, 16, 0.6); + border: 1px solid #2f2517; + padding: 3px 9px; + border-radius: 2px; +} +.res-icon { + font-size: 0.95rem; +} +.topbar .btn-sm { + margin-left: 6px; +} + +/* ---------- tabs ---------- */ +.tabs { + display: flex; + gap: 3px; + padding: 0 16px; + background: linear-gradient(180deg, #181209, #110d08); + border-bottom: 1px solid #2b2214; + flex-shrink: 0; +} +.tab { + padding: 9px 20px; + color: #8f7f5d; + cursor: pointer; + border-bottom: 2px solid transparent; + letter-spacing: 4px; + user-select: none; + font-size: 0.98rem; + position: relative; + transition: color 0.15s; +} +.tab:hover { + color: #c9b787; +} +.tab.sel { + color: #e8cf96; + border-bottom-color: var(--vermilion); + text-shadow: 0 0 18px rgba(192, 90, 65, 0.35); +} +.tab.sel::before { + content: "〓"; + margin-right: 8px; + font-size: 0.6rem; + color: var(--vermilion-soft); + vertical-align: 2px; +} + +.main { + flex: 1; + display: flex; + overflow: hidden; +} +.main-left { + flex: 1; + overflow-y: auto; + padding: 16px; + background: + radial-gradient(ellipse at 15% 8%, rgba(64, 48, 24, 0.5) 0%, transparent 55%), + radial-gradient(ellipse at 85% 90%, rgba(46, 34, 18, 0.55) 0%, transparent 50%), + #110d08; +} +.main-right { + width: 330px; + border-left: 1px solid #2b2214; + background: linear-gradient(180deg, #0d0a06, #120e09); + display: flex; + flex-direction: column; + flex-shrink: 0; + overflow: hidden; +} + +/* ============================================================ + 家族成员 + ============================================================ */ +.member-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); + gap: 12px; +} +.member-card { + position: relative; + background: + url("data:image/svg+xml;utf8,"), + linear-gradient(180deg, #f4ecd9 0%, #e9dabc 100%); + color: var(--ink); + border: 1px solid #b9a77e; + border-radius: 3px; + padding: 12px 12px 10px; + cursor: pointer; + transition: all 0.18s; + box-shadow: + 0 4px 12px rgba(0, 0, 0, 0.55), + inset 0 0 0 1px rgba(255, 252, 242, 0.6), + inset 0 0 32px rgba(130, 100, 50, 0.1); +} +.member-card:hover { + border-color: var(--gold); + transform: translateY(-2px); + box-shadow: + 0 10px 22px rgba(0, 0, 0, 0.55), + 0 0 0 1px rgba(226, 197, 122, 0.25), + inset 0 0 0 1px rgba(255, 252, 242, 0.6); +} +.member-card.dead { + opacity: 0.5; + filter: saturate(0.55); +} +.member-card.dead:hover { + opacity: 0.65; +} +.mc-head { + display: flex; + align-items: center; + gap: 10px; +} +.m-avatar { + width: 46px; + height: 46px; + border-radius: 50%; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.36rem; + color: #f8efd9; + font-weight: 700; + background: radial-gradient(circle at 34% 28%, #b6503d, #8c2f22 70%); + border: 2px solid rgba(249, 236, 208, 0.85); + box-shadow: + 0 3px 8px rgba(80, 20, 10, 0.5), + inset 0 0 10px rgba(90, 22, 12, 0.55); + text-shadow: 0 1px 2px rgba(70, 15, 8, 0.7); + font-family: "STXingkai", "STKaiti", "KaiTi", serif; +} +.member-name { + display: flex; + align-items: center; + gap: 8px; + font-size: 1.12rem; + color: #2e2415; + font-weight: 700; + letter-spacing: 1px; +} +.member-realm { + font-size: 0.82rem; + color: #6d5b3e; + margin-top: 2px; +} +.member-tags { + display: flex; + gap: 5px; + flex-wrap: wrap; + margin-top: 6px; +} +.mc-foot { + margin-top: 8px; +} +.mc-foot .bar { + height: 6px; +} + +.tag { + background: rgba(44, 33, 18, 0.85); + border: 1px solid #5a4727; + color: #d9c49a; + font-size: 0.72rem; + padding: 1px 7px; + border-radius: 2px; + letter-spacing: 0.5px; +} +.tag.gold-t { + background: rgba(140, 90, 22, 0.9); + border-color: var(--gold); + color: #ffe9b0; +} +.tag.head-t { + background: var(--vermilion); + border-color: #d9825f; + color: #ffeedd; + box-shadow: 0 0 8px rgba(169, 59, 46, 0.4); +} + +/* ---------- 军情 feed ---------- */ +.feed { + padding: 10px; + overflow-y: auto; + flex: 1; +} +.feed-item { + padding: 5px 8px; + border-left: 2px solid transparent; + margin-bottom: 3px; + font-size: 0.85rem; + line-height: 1.5; + border-radius: 2px; +} +.feed-item:hover { + background: rgba(255, 235, 190, 0.03); +} +.feed-item.k-info { + color: #8b7c5e; +} +.feed-item.k-good { + border-left-color: #86a06a; + color: #b3c99a; +} +.feed-item.k-bad { + border-left-color: var(--vermilion); + color: #d99a85; +} +.feed-item.k-war { + border-left-color: #c28552; + color: #e0b98a; +} +.feed-item.k-chronicle { + border-left-color: var(--gold); + color: #e0c688; +} +.feed-item.k-event { + border-left-color: #7d93a4; + color: #a9bfcf; +} +.feed-date { + color: #5c5140; + font-size: 0.72rem; + margin-right: 6px; +} +.feed-title { + padding: 10px 12px 6px; + color: #caa95c; + letter-spacing: 5px; + font-size: 0.85rem; + border-bottom: 1px solid #29200f; + background: linear-gradient(180deg, #151009, #100c07); +} + +/* ============================================================ + 领地 + ============================================================ */ +.bld-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(248px, 1fr)); + gap: 12px; +} +.bld-card { + background: + linear-gradient(180deg, #f4ecd9 0%, #e7d7b6 100%); + color: var(--ink); + border: 1px solid #b9a77e; + border-radius: 3px; + padding: 14px; + display: flex; + flex-direction: column; + min-height: 190px; + box-shadow: + 0 4px 12px rgba(0, 0, 0, 0.55), + inset 0 0 0 1px rgba(255, 252, 242, 0.6), + inset 0 0 36px rgba(130, 100, 50, 0.1); +} +.bld-head { + display: flex; + align-items: center; + gap: 9px; + font-size: 1.06rem; + font-weight: 700; + color: #3a2d17; +} +.bld-icon { + font-size: 1.5rem; + filter: saturate(0.8); +} +.bld-lv { + margin-left: auto; + color: #a04b33; + letter-spacing: 2px; + font-size: 0.9rem; +} +.bld-desc { + color: #6d5b3e; + font-size: 0.85rem; + margin: 7px 0; + min-height: 38px; + line-height: 1.5; +} +.bld-prod { + font-size: 0.85rem; + color: #3e5a66; + margin-bottom: 10px; + border-top: 1px dashed rgba(90, 72, 40, 0.35); + padding-top: 7px; +} +.bld-card.uc { /* 未建 */ + background: + linear-gradient(180deg, rgba(244, 236, 217, 0.45), rgba(231, 215, 182, 0.3)); + border-style: dashed; + color: #4a3d29; + filter: saturate(0.6); +} + +/* ============================================================ + 坊市 / 外交 / 探秘 + ============================================================ */ +.market-table { + width: 100%; + border-collapse: collapse; + background: + linear-gradient(180deg, #f4ecd9, #e9dabc); + color: var(--ink); + border: 1px solid #b9a77e; + border-radius: 3px; + overflow: hidden; + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.55), inset 0 0 0 1px rgba(255, 252, 242, 0.55); +} +.market-table th { + text-align: left; + color: #64451e; + font-weight: 700; + background: rgba(120, 92, 42, 0.12); + border-bottom: 1px solid #b3a075; + padding: 8px 10px; + font-size: 0.9rem; + letter-spacing: 2px; +} +.market-table td { + padding: 8px 10px; + border-bottom: 1px dashed rgba(120, 98, 55, 0.3); + font-size: 0.92rem; +} +.market-table tr:hover td { + background: rgba(179, 145, 62, 0.08); +} +.market-table .btn { + background: linear-gradient(180deg, #efe4c8, #e2d2ab); + border-color: #a08c5c; + color: #4a3620; +} +.market-table .btn:hover { + border-color: var(--vermilion); + color: #8a2f22; +} + +.help-text { + color: #a2906c; + font-size: 0.85rem; + line-height: 1.8; + max-width: 680px; + background: rgba(24, 18, 10, 0.72); + border: 1px solid #33291a; + border-left: 3px solid var(--vermilion); + padding: 11px 14px; + border-radius: 2px; + margin: 0 0 14px; +} + +.npc-row { + display: flex; + align-items: center; + gap: 14px; + padding: 12px 14px; + background: linear-gradient(180deg, #f4ecd9, #e9dabc); + color: var(--ink); + border: 1px solid #b9a77e; + border-radius: 3px; + margin-bottom: 8px; + box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5); +} +.npc-name { + min-width: 136px; + font-size: 1.05rem; + font-weight: 700; + color: #3a2d17; +} +.relation-bar { + flex: 1; +} +.relation-num { + width: 92px; + text-align: right; + font-size: 0.85rem; +} +.npc-row .btn { + background: linear-gradient(180deg, #efe4c8, #dfcfa8); + border-color: #a08c5c; + color: #4a3620; +} +.npc-row .btn:hover { + border-color: var(--vermilion); + color: #8a2f22; +} + +.mission-card { + background: linear-gradient(180deg, #f4ecd9, #e9dabc); + color: var(--ink); + border: 1px solid #b9a77e; + border-radius: 3px; + padding: 14px; + margin-bottom: 10px; + box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5); +} +.squad-picker { + display: flex; + gap: 7px; + flex-wrap: wrap; + margin: 8px 0; +} +.squadian { + padding: 4px 10px; + border: 1px solid #a08c5c; + border-radius: 2px; + cursor: pointer; + font-size: 0.85rem; + color: #64502e; + background: #f7f0de; + transition: all 0.14s; +} +.squadian:hover { + border-color: var(--gold); +} +.squadian.sel { + border-color: var(--vermilion); + color: #9c3526; + background: rgba(169, 59, 46, 0.1); +} + +/* ============================================================ + 史书 + ============================================================ */ +.chronicle-list { + max-width: 760px; + background: linear-gradient(180deg, #f4ecd9, #e9dabc); + color: var(--ink); + border: 1px solid #b9a77e; + border-radius: 3px; + padding: 6px 16px; + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.55), inset 0 0 40px rgba(130, 100, 50, 0.1); +} +.ch-item { + display: flex; + gap: 14px; + padding: 9px 0; + border-bottom: 1px dashed rgba(120, 98, 55, 0.32); + font-size: 0.95rem; + line-height: 1.6; + color: #3c2f1a; +} +.ch-item:last-child { + border-bottom: none; +} +.ch-year { + color: #a04b33; + white-space: nowrap; + min-width: 92px; + letter-spacing: 1px; +} +.ch-item .tag { + background: rgba(120, 92, 42, 0.16); + border-color: #b3a075; + color: #7a5d2e; +} + +/* ============================================================ + 弹出镸面 + ============================================================ */ +.modal-outer { + position: fixed; + inset: 0; + background: rgba(5, 4, 2, 0.82); + backdrop-filter: blur(2px); + display: flex; + align-items: center; + justify-content: center; + z-index: 50; +} +.modal { + position: relative; + width: 570px; + max-height: 82vh; + overflow-y: auto; + border-radius: 2px; + padding: 26px 30px 24px; + background: + url("data:image/svg+xml;utf8,"), + linear-gradient(180deg, #f7efdd 0%, #ecdfc0 100%); + color: var(--ink); + box-shadow: + 0 26px 60px rgba(0, 0, 0, 0.85), + inset 0 0 0 1px rgba(255, 251, 240, 0.65), + inset 0 0 60px rgba(130, 100, 50, 0.13); + border: 1px solid #9c8a60; +} +.modal::before, +.modal::after { + content: ""; + position: absolute; + left: -1px; + right: -1px; + height: 9px; + background: linear-gradient(180deg, #6b5638, #4b3b24); + border: 1px solid #8a7248; + border-radius: 3px; + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.6); + pointer-events: none; + z-index: 3; +} +.modal::before { + top: -5px; +} +.modal::after { + bottom: -5px; +} +.modal-title { + font-size: 1.5rem; + color: #8c2f22; + letter-spacing: 6px; + text-align: center; + margin-bottom: 5px; + font-weight: 700; + text-shadow: 0 1px 0 rgba(255, 250, 235, 0.8); +} +.modal-cat { + text-align: center; + color: #97835c; + font-size: 0.82rem; + margin-bottom: 14px; + letter-spacing: 2px; +} +.modal-text { + font-size: 1.03rem; + line-height: 1.9; + margin-bottom: 18px; + color: #342a18; + border-top: 1px solid rgba(169, 59, 46, 0.25); + padding-top: 14px; + border-bottom: 1px solid rgba(120, 98, 55, 0.25); + padding-bottom: 14px; +} +.modal-opts { + display: flex; + flex-direction: column; + gap: 9px; +} +.opt-btn { + background: #fbf5e5; + border: 1px solid #b3a075; + padding: 11px 16px; + border-radius: 2px; + cursor: pointer; + text-align: left; + font-family: inherit; + color: #342a18; + font-size: 0.98rem; + transition: all 0.15s; + box-shadow: inset 0 1px 0 rgba(255, 255, 250, 0.6); +} +.opt-btn:hover { + border-color: var(--vermilion); + background: #fdf8ea; + box-shadow: 0 0 0 1px rgba(169, 59, 46, 0.15), inset 0 1px 0 rgba(255, 255, 250, 0.6); +} +.opt-btn:hover .hint { + color: #9c3526; +} +.opt-btn .hint { + display: block; + color: #97835c; + font-size: 0.8rem; + margin-top: 3px; +} + +/* ---------- member modal ---------- */ +.member-modal { + width: 650px; +} +.mm-sec { + margin-top: 12px; + border-top: 1px dashed rgba(120, 98, 55, 0.35); + padding-top: 10px; +} +.mm-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3px 14px; + font-size: 0.94rem; +} +.mm-line { + display: flex; + justify-content: space-between; + padding: 2px 0; + color: #6d5b3e; +} +.mm-line b { + color: #342a18; + font-weight: 700; +} + +/* ---------- 战报 · 砚阵 ---------- */ +.battle-modal { + width: 640px; + background: + url("data:image/svg+xml;utf8,"), + linear-gradient(180deg, #1b1410 0%, #120d09 100%); + border-color: #4d3e2a; + color: #e8dbba; +} +.battle-modal::before, +.battle-modal::after { + background: linear-gradient(180deg, #3a2f1e, #241c11); + border-color: #5b4a2e; +} +.battle-modal .modal-title { + color: #d8b879; + text-shadow: 0 2px 6px rgba(0, 0, 0, 0.8); +} +.battle-modal .modal-cat { + color: #8f7f5d; +} +.battle-lines { + background: rgba(10, 8, 5, 0.75); + border: 1px solid #3a3021; + border-radius: 3px; + padding: 16px; + line-height: 1.9; + font-size: 0.97rem; + color: #d9c99f; + margin-bottom: 14px; + box-shadow: inset 0 2px 12px rgba(0, 0, 0, 0.7); +} +.battle-lines div:first-child { + color: var(--gold-soft); + letter-spacing: 2px; +} + +/* ============================================================ + 设置 & 杂项 + ============================================================ */ +.set-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 11px 4px; + border-bottom: 1px dashed rgba(144, 122, 76, 0.3); + max-width: 660px; + color: #cdbd97; + font-size: 0.96rem; +} + +.toast { + position: fixed; + bottom: 26px; + left: 50%; + transform: translateX(-50%); + background: linear-gradient(180deg, #2b1f10, #1c1409); + border: 1px solid var(--gold); + color: var(--gold-soft); + padding: 9px 24px; + border-radius: 2px; + z-index: 99; + font-size: 0.95rem; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.7); + letter-spacing: 2px; +} + +.gameover-banner { + text-align: center; + padding: 70px 20px; +} +.gameover-banner h1 { + color: #a9402f; + letter-spacing: 14px; + font-size: 2.8rem; + margin-bottom: 16px; + text-shadow: 0 4px 18px rgba(0, 0, 0, 0.8); +} diff --git a/tests/events.helpers.ts b/tests/events.helpers.ts new file mode 100644 index 0000000..04b1639 --- /dev/null +++ b/tests/events.helpers.ts @@ -0,0 +1,7 @@ +import { World } from '../src/renderer/game/engine/world' +import { matchesCond } from '../src/renderer/game/engine/systems/events' +import { Cond } from '../src/renderer/game/data/events' + +export function matchesCondPub(w: World, cond?: Cond): boolean { + return matchesCond(w, cond) +} diff --git a/tests/rng.test.ts b/tests/rng.test.ts new file mode 100644 index 0000000..76944b8 --- /dev/null +++ b/tests/rng.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from 'vitest' +import { Rng, seedToRng } from '../src/renderer/game/core/rng' + +describe('Rng', () => { + it('is deterministic for same seed', () => { + const a = new Rng(seedToRng('hello')) + const b = new Rng(seedToRng('hello')) + for (let i = 0; i < 100; i++) { + expect(a.next()).toBe(b.next()) + } + }) + + it('differs for different seeds', () => { + const a = new Rng(seedToRng('aaa')) + const b = new Rng(seedToRng('bbb')) + const seq = 20 + let diff = false + for (let i = 0; i < seq; i++) { + if (a.next() !== b.next()) diff = true + } + expect(diff).toBe(true) + }) + + it('produces values in [0, 1)', () => { + const rng = new Rng(seedToRng('x')) + for (let i = 0; i < 1000; i++) { + const v = rng.next() + expect(v).toBeGreaterThanOrEqual(0) + expect(v).toBeLessThan(1) + } + }) + + it('int works within bounds', () => { + const rng = new Rng(seedToRng('y')) + for (let i = 0; i < 500; i++) { + const v = rng.int(0, 10) + expect(v).toBeGreaterThanOrEqual(0) + expect(v).toBeLessThanOrEqual(10) + expect(Number.isInteger(v)).toBe(true) + } + }) + + it('state roundtrip continues identically', () => { + const a = new Rng(seedToRng('z')) + const b = new Rng(seedToRng('z')) + for (let i = 0; i < 10; i++) { + a.next() + b.next() + } + const snap = a.getState() + const c = new Rng(snap) + expect(c.next()).toBe(b.next()) + expect(c.next()).toBe(b.next()) + }) +}) diff --git a/tests/systems.test.ts b/tests/systems.test.ts new file mode 100644 index 0000000..bbfa3b5 --- /dev/null +++ b/tests/systems.test.ts @@ -0,0 +1,91 @@ +import { describe, expect, it } from 'vitest' +import { World } from '../src/renderer/game/engine/world' +import { combatPowerOf } from '../src/renderer/game/engine/systems/combat' +import { marketPrice, buyItem, sellItem, buyTechnique } from '../src/renderer/game/engine/market' +import { resolveBreakthrough } from '../src/renderer/game/engine/systems/cultivation' +import { matchesCondPub } from './events.helpers' + +describe('Combat power', () => { + it('scales with realm', () => { + const w = World.create({ seed: 'p', surname: '赵', familyName: '赵家', motto: 'm', difficulty: 'normal' }) + const head = w.head() + const power = combatPowerOf(w, head) + expect(power).toBeGreaterThan(3) + power > 0 + }) + + it('higher realm beats lower realm', () => { + const w = World.create({ seed: 'p2', surname: '赵', familyName: '赵家', motto: 'm', difficulty: 'normal' }) + const low = w.state.members['x5'] + const high = w.state.members['x4'] + expect(combatPowerOf(w, high)).toBeGreaterThan(combatPowerOf(w, low)) + }) +}) + +describe('Market', () => { + it('buy/sell roundtrip keeps stones consistent', () => { + const w = World.create({ seed: 'mk', surname: '王', familyName: '王家', motto: 'm', difficulty: 'normal' }) + const fam = w.state.family + const price = marketPrice(w, 'lingcao') + const stones0 = fam.stones + const buyOk = buyItem(w, 'lingcao', 11) + expect(buyOk).toBe(stones0 >= price * 11) + if (buyOk) { + expect(fam.inventory['lingcao']).toBe(60 + 11) + expect(fam.stones).toBe(stones0 - price * 11) + sellItem(w, 'lingcao', 11) + expect(fam.stones).toBe(stones0) + } + }) + + it('cannot buy beyond treasury', () => { + const w = World.create({ seed: 'mk2', surname: '王', familyName: '王家', motto: 'm', difficulty: 'hard' }) + w.state.family.stones = 10 + expect(buyItem(w, 'lingcao', 999)).toBe(false) + }) + + it('buy technique adds to library once', () => { + const w = World.create({ seed: 'mk3', surname: '王', familyName: '王家', motto: 'm', difficulty: 'normal' }) + w.state.family.stones = 5000 + expect(buyTechnique(w, 't-zhenyu', 700)).toBe(true) + expect(w.state.family.techniques.includes('t-zhenyu')).toBe(true) + expect(buyTechnique(w, 't-zhenyu', 700)).toBe(false) + }) +}) + +describe('Breakthrough', () => { + it('advances realm on success', () => { + const w = World.create({ seed: 'bt', surname: '李', familyName: '李家', motto: 'm', difficulty: 'easy' }) + const c = w.state.members['x5'] + c.realm = { major: 'mortal', minor: 0 } + c.realmProgress = 100 + c.perception = 9 + c.mind = 9 + resolveBreakthrough(w, c, 0.3) + expect(c.realm.major).toBe('qi') + }) + + it('failed breakthrough costs progress but not death', () => { + const w = World.create({ seed: 'bt2', surname: '李', familyName: '李家', motto: 'm', difficulty: 'normal' }) + const c = w.state.members['x5'] + c.realm = { major: 'mortal', minor: 0 } + c.realmProgress = 100 + resolveBreakthrough(w, c, -0.5) + // chance was min-clamped; both outcomes acceptable, but check invariants + if (c.realm.major === 'mortal') { + expect(c.realmProgress).toBeLessThan(100) + } else { + expect(c.realmProgress).toBe(0) + } + }) +}) + +describe('Event conditions', () => { + it('leading event ids exist', () => { + const w = World.create({ seed: 'evc', surname: '刘', familyName: '刘家', motto: 'm', difficulty: 'normal' }) + expect(matchesCondPub(w, { minBuilding: { id: 'lingtian', level: 1 } })).toBe(true) + expect(matchesCondPub(w, { minBuilding: { id: 'danfang', level: 1 } })).toBe(false) + expect(matchesCondPub(w, { minAdult: 3 })).toBe(true) + expect(matchesCondPub(w, { maxAdult: 2 })).toBe(false) + }) +}) diff --git a/tests/world.helpers.ts b/tests/world.helpers.ts new file mode 100644 index 0000000..3e82575 --- /dev/null +++ b/tests/world.helpers.ts @@ -0,0 +1,21 @@ +import { World, WorldEventBus } from '../src/renderer/game/engine/world' +import { LogItem, ChronicleEntry, BattleLog } from '../src/renderer/game/types/domain' + +const sinks: Set = new Set() + +resetSaveBus() +export function resetSaveBus(): void { + sinks.clear() +} + +export function attachLogSink(w: World): void { + const bus: WorldEventBus = { + onLog: () => undefined, + onChronicle: () => undefined, + onBattle: () => undefined, + onPendingEvent: () => undefined, + onGameOver: () => undefined + } + sinks.add(bus) + w.out.push(bus) +} diff --git a/tests/world.test.ts b/tests/world.test.ts new file mode 100644 index 0000000..2e719a5 --- /dev/null +++ b/tests/world.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, it } from 'vitest' +import { World } from '../src/renderer/game/engine/world' +import { resetSaveBus, attachLogSink } from './world.helpers' + +describe('World engine', () => { + it('creates a valid starting family', () => { + const w = World.create({ seed: 't1', surname: '林', familyName: '林家', motto: 'm', difficulty: 'normal' }) + expect(Object.values(w.state.members).length).toBe(5) + expect(w.aliveMembers().length).toBe(5) + expect(w.state.family.headId).toBe('x1') + expect(w.ageOf(w.head())).toBe(35) + }) + + it('survives 20 years (240 months) without NaN or throws', () => { + const w = World.create({ seed: 'smoke', surname: '沈', familyName: '沈氏', motto: 'x', difficulty: 'normal' }) + resetSaveBus() + attachLogSink(w) + for (let i = 0; i < 240; i++) { + if (w.state.gameOver) break + w.advanceMonth() + } + const s = w.state + expect(Number.isNaN(s.family.stones)).toBe(false) + expect(s.year).toBeGreaterThan(10) + for (const c of Object.values(s.members)) { + expect(Number.isNaN(c.realmProgress)).toBe(false) + expect(Number.isNaN(c.health)).toBe(false) + } + }) + + it('same seed plays identically (deterministic)', () => { + const a = World.create({ seed: 'det', surname: '苏', familyName: '苏家', motto: 'm', difficulty: 'easy' }) + const b = World.create({ seed: 'det', surname: '苏', familyName: '苏家', motto: 'm', difficulty: 'easy' }) + resetSaveBus() + attachLogSink(a) + resetSaveBus() + attachLogSink(b) + const ignoreRng = (state: { rng: unknown }) => JSON.parse(JSON.stringify(state)) + for (let i = 0; i < 60; i++) { + a.advanceMonth() + b.advanceMonth() + } + a.state.pendingEvent && (a.state.pendingEvent = undefined) + b.state.pendingEvent && (b.state.pendingEvent = undefined) + const x = ignoreRng({ ...a.state, pendingEvent: undefined }) + const y = ignoreRng({ ...b.state, pendingEvent: undefined }) + expect(JSON.stringify(x)).toBe(JSON.stringify(y)) + }) + + it('families grow over time (births occur)', () => { + const w = World.create({ seed: 'grow', surname: '叶', familyName: '叶家', motto: 'm', difficulty: 'easy' }) + resetSaveBus() + attachLogSink(w) + for (let i = 0; i < 240; i++) { + if (w.state.gameOver) break + w.advanceMonth() + } + expect(Object.keys(w.state.members).length).toBeGreaterThan(5) + }) + + it('game over when everyone dies', () => { + const w = World.create({ seed: 'diehard', surname: '麦', familyName: '麦家', motto: 'm', difficulty: 'hard' }) + resetSaveBus() + attachLogSink(w) + for (const c of Object.values(w.state.members)) { + c.alive = false + } + for (let i = 0; i < 5; i++) w.advanceMonth() + expect(w.state.gameOver).toBeTruthy() + }) + + it('building upgrade consumes resources', () => { + const w = World.create({ seed: 'bld', surname: '金', familyName: '金家', motto: 'm', difficulty: 'easy' }) + const before = w.state.family.inventory['lingkuang'] ?? 0 + w.state.family.inventory['lingkuang'] = (w.state.family.inventory['lingkuang'] ?? 0) + 100 + const ok = w.build('fangshi') + expect(ok).toBe(true) + const upgraded = w.upgrade('fangshi') + expect(upgraded).toBe(true) + expect(w.state.family.buildings['fangshi']).toBe(2) + expect((w.state.family.inventory['lingkuang'] ?? 0) - before).toBeLessThan(100) + }) +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..68e27f0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "allowJs": false, + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "jsx": "react-jsx", + "isolatedModules": true, + "types": ["node", "vite/client"], + "noEmit": true + }, + "include": ["src", "electron.vite.config.ts", "vitest.config.ts"] +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..b268924 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['tests/**/*.test.ts'], + environment: 'node', + pool: 'forks' + } +})