diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/data/hotupdate.db b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/data/hotupdate.db new file mode 100644 index 00000000..8576b1aa Binary files /dev/null and b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/data/hotupdate.db differ diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/package-lock.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/package-lock.json new file mode 100644 index 00000000..2eb4eee1 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/package-lock.json @@ -0,0 +1,3242 @@ +{ + "name": "cocos-ecs-hotupdate-admin", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cocos-ecs-hotupdate-admin", + "version": "1.0.0", + "dependencies": { + "adm-zip": "^0.5.10", + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "crypto": "^1.0.1", + "express": "^4.18.2", + "fs-extra": "^11.1.1", + "jsonwebtoken": "^9.0.2", + "moment": "^2.29.4", + "multer": "^1.4.5-lts.1", + "node-cron": "^3.0.3", + "sqlite3": "^5.1.6" + }, + "devDependencies": { + "@types/adm-zip": "^0.5.0", + "@types/bcryptjs": "^2.4.2", + "@types/cors": "^2.8.13", + "@types/express": "^4.17.17", + "@types/fs-extra": "^11.0.1", + "@types/jsonwebtoken": "^9.0.2", + "@types/multer": "^1.4.7", + "@types/node": "^20.5.0", + "@types/node-cron": "^3.0.8", + "ts-node-dev": "^2.0.0", + "typescript": "^5.1.6" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/adm-zip": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz", + "integrity": "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true + }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true + }, + "node_modules/@types/multer": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", + "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "20.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.1.tgz", + "integrity": "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==", + "dev": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-cron": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/node-cron/-/node-cron-3.0.11.tgz", + "integrity": "sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", + "dev": true + }, + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "devOptional": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "devOptional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "devOptional": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "optional": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "optional": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "devOptional": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "optional": true, + "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/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "devOptional": true, + "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-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "optional": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/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.", + "devOptional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "optional": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "optional": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "optional": true + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "optional": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "devOptional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/multer": { + "version": "1.4.5-lts.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", + "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", + "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" + }, + "node_modules/node-cron": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz", + "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", + "dependencies": { + "uuid": "8.3.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.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": ">= 10.12.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "optional": true, + "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-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "optional": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", + "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", + "optional": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "optional": true + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "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.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "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-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "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-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node-dev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.1", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.6", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^10.4.0", + "tsconfig": "^7.0.0" + }, + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/ts-node-dev/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-node-dev/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/package.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/package.json new file mode 100644 index 00000000..0eaf9dee --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/package.json @@ -0,0 +1,39 @@ +{ + "name": "cocos-ecs-hotupdate-admin", + "version": "1.0.0", + "description": "Cocos ECS Extension Hot Update Management Backend", + "main": "dist/server.js", + "scripts": { + "dev": "ts-node-dev --respawn --transpile-only src/server.ts", + "build": "tsc", + "start": "node dist/server.js", + "migrate": "ts-node src/database/migrate.ts", + "simple": "node server.js" + }, + "dependencies": { + "express": "^4.18.2", + "multer": "^1.4.5-lts.1", + "sqlite3": "^5.1.6", + "cors": "^2.8.5", + "bcryptjs": "^2.4.3", + "jsonwebtoken": "^9.0.2", + "adm-zip": "^0.5.10", + "crypto": "^1.0.1", + "fs-extra": "^11.1.1", + "moment": "^2.29.4", + "node-cron": "^3.0.3" + }, + "devDependencies": { + "@types/express": "^4.17.17", + "@types/multer": "^1.4.7", + "@types/node": "^20.5.0", + "@types/cors": "^2.8.13", + "@types/bcryptjs": "^2.4.2", + "@types/jsonwebtoken": "^9.0.2", + "@types/adm-zip": "^0.5.0", + "@types/fs-extra": "^11.0.1", + "@types/node-cron": "^3.0.8", + "typescript": "^5.1.6", + "ts-node-dev": "^2.0.0" + } +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/public/index.html b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/public/index.html new file mode 100644 index 00000000..48928682 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/public/index.html @@ -0,0 +1,686 @@ + + + + + + Cocos ECS Extension - 热更新管理后台 + + + + +
+
+

🚀 Cocos ECS Extension - 热更新管理后台

+
+ +
+ +
+
+

🔌 插件管理

+
+
+
+ {{ plugin.icon }} +
+

{{ plugin.displayName }}

+

{{ plugin.description || '暂无描述' }}

+
+ {{ plugin.status === 'active' ? '✅ 活跃' : '⏸️ 暂停' }} + 📦 版本: {{ plugin.versionCount || 0 }} + 📥 下载: {{ plugin.totalDownloads || 0 }} +
+

{{ plugin.author || '未知作者' }}

+
+
+
+
+
+
+ + +
+
+

📊 系统统计

+
+
+
{{ stats.totalVersions }}
+
总版本数
+
+
+
{{ stats.activeUsers }}
+
活跃用户
+
+
+
{{ stats.todayUpdates }}
+
今日更新
+
+
+
{{ stats.successRate }}%
+
成功率
+
+
+
+
+ + +
+
+

📦 上传新版本

+
+
+ 📁 +

点击或拖拽文件到此处上传插件包

+

支持 .zip 格式,最大 100MB

+
+
+ +

上传中... {{ uploadProgress }}%

+
+
+ + +
+

版本信息

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+
+ +
+
+
+
+ + +
+
+
+

📋 版本管理 - {{ getCurrentPluginName() }}

+
+ + + +
+
+ +
+
+
+

+ {{ getPluginIcon(version.pluginId) }} + {{ version.version }} ({{ getChannelName(version.channel) }}) + - {{ getPluginDisplayName(version.pluginId) }} +

+

{{ version.description }}

+

发布时间: {{ formatDate(version.release_date) }} | 文件大小: {{ formatSize(version.file_size) }}

+
+
+ + {{ getStatusName(version.status) }} + + + +
+
+
+ {{ currentPlugin ? '该插件暂无版本数据' : '暂无版本数据' }} +
+
+
+
+
+ + + +
+ + + + + \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/database/index.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/database/index.ts new file mode 100644 index 00000000..555ab556 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/database/index.ts @@ -0,0 +1,283 @@ +import * as sqlite3 from 'sqlite3'; +import { promisify } from 'util'; +import * as path from 'path'; +import * as fs from 'fs-extra'; + +// 数据库实例 +export let db: sqlite3.Database; + +/** + * 插件信息接口 + */ +export interface PluginInfo { + id: string; + name: string; + displayName: string; + description: string; + author: string; + repository: string; + icon: string; + status: 'active' | 'inactive'; +} + +/** + * 初始化数据库 + */ +export async function initDatabase(): Promise { + const dbPath = path.join(__dirname, '../../data/hotupdate.db'); + + // 确保数据目录存在 + await fs.ensureDir(path.dirname(dbPath)); + + return new Promise((resolve, reject) => { + db = new sqlite3.Database(dbPath, (err: any) => { + if (err) { + console.error('数据库连接失败:', err); + reject(err); + return; + } + + console.log('✅ 数据库连接成功'); + createTables().then(resolve).catch(reject); + }); + }); +} + +/** + * 创建数据表 + */ +async function createTables(): Promise { + const run = promisify(db.run.bind(db)); + + try { + // 插件信息表 + await run(` + CREATE TABLE IF NOT EXISTS plugins ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + display_name TEXT NOT NULL, + description TEXT, + author TEXT, + repository TEXT, + icon TEXT, + status TEXT DEFAULT 'active', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `); + + // 版本信息表 - 添加plugin_id字段 + await run(` + CREATE TABLE IF NOT EXISTS versions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + plugin_id TEXT NOT NULL, + version TEXT NOT NULL, + channel TEXT NOT NULL DEFAULT 'stable', + release_date DATETIME DEFAULT CURRENT_TIMESTAMP, + description TEXT, + download_url TEXT NOT NULL, + file_size INTEGER NOT NULL, + checksum TEXT NOT NULL, + mandatory BOOLEAN DEFAULT 0, + status TEXT DEFAULT 'active', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (plugin_id) REFERENCES plugins (id) ON DELETE CASCADE, + UNIQUE(plugin_id, version, channel) + ) + `); + + // 版本文件表 + await run(` + CREATE TABLE IF NOT EXISTS version_files ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + version_id INTEGER NOT NULL, + file_path TEXT NOT NULL, + file_hash TEXT NOT NULL, + file_size INTEGER NOT NULL, + action TEXT NOT NULL DEFAULT 'update', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (version_id) REFERENCES versions (id) ON DELETE CASCADE + ) + `); + + // 用户表 + await run(` + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + role TEXT DEFAULT 'admin', + last_login DATETIME, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `); + + // 更新统计表 - 添加plugin_id字段 + await run(` + CREATE TABLE IF NOT EXISTS update_stats ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + plugin_id TEXT NOT NULL, + version TEXT NOT NULL, + user_count INTEGER DEFAULT 0, + success_count INTEGER DEFAULT 0, + failure_count INTEGER DEFAULT 0, + last_updated DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (plugin_id) REFERENCES plugins (id) ON DELETE CASCADE + ) + `); + + // 配置表 + await run(` + CREATE TABLE IF NOT EXISTS config ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + description TEXT, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `); + + console.log('✅ 数据表创建成功'); + + // 插入默认数据 + await insertDefaultData(); + + } catch (error) { + console.error('❌ 数据表创建失败:', error); + throw error; + } +} + +/** + * 插入默认数据 + */ +async function insertDefaultData(): Promise { + const run = promisify(db.run.bind(db)) as (sql: string, params?: any[]) => Promise; + + // 插入默认插件 + const defaultPlugins: PluginInfo[] = [ + { + id: 'cocos-ecs-extension', + name: 'cocos-ecs-extension', + displayName: 'Cocos ECS Framework', + description: '为Cocos Creator提供高性能ECS框架支持', + author: 'esengine', + repository: 'https://github.com/esengine/ecs-framework', + icon: '🎮', + status: 'active' + }, + { + id: 'behaviour-tree-ai', + name: 'behaviour-tree-ai', + displayName: 'Behaviour Tree AI', + description: '智能行为树AI系统,支持可视化编辑', + author: 'esengine', + repository: 'https://github.com/esengine/ecs-framework/tree/master/thirdparty/BehaviourTree-ai', + icon: '🧠', + status: 'active' + } + ]; + + for (const plugin of defaultPlugins) { + try { + await run( + `INSERT OR IGNORE INTO plugins + (id, name, display_name, description, author, repository, icon, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [plugin.id, plugin.name, plugin.displayName, plugin.description, + plugin.author, plugin.repository, plugin.icon, plugin.status] + ); + } catch (error) { + console.warn('插件数据插入失败:', plugin.id); + } + } + + // 插入默认配置 + const defaultConfigs = [ + { key: 'server_url', value: 'http://localhost:3001', description: '服务器地址' }, + { key: 'cdn_url', value: 'https://cdn.earthonline-game.cn', description: 'CDN地址' }, + { key: 'auto_backup', value: 'true', description: '自动备份' }, + { key: 'max_backup_count', value: '5', description: '最大备份数量' }, + { key: 'upload_max_size', value: '100', description: '上传文件最大大小(MB)' } + ]; + + for (const config of defaultConfigs) { + try { + await run( + 'INSERT OR IGNORE INTO config (key, value, description) VALUES (?, ?, ?)', + [config.key, config.value, config.description] + ); + } catch (error) { + console.warn('配置插入失败:', config.key); + } + } + + // 插入默认管理员用户 + const bcrypt = require('bcryptjs'); + const defaultPassword = 'admin123'; + const hashedPassword = await bcrypt.hash(defaultPassword, 10); + + try { + await run( + 'INSERT OR IGNORE INTO users (username, password_hash, role) VALUES (?, ?, ?)', + ['admin', hashedPassword, 'admin'] + ); + console.log('✅ 默认管理员账户创建成功 (用户名: admin, 密码: admin123)'); + } catch (error) { + console.warn('默认用户创建失败:', error); + } +} + + + +/** + * 执行查询 + */ +export function query(sql: string, params: any[] = []): Promise { + return new Promise((resolve, reject) => { + db.all(sql, params, (err: any, rows: any) => { + if (err) { + reject(err); + } else { + resolve(rows); + } + }); + }); +} + +/** + * 执行单个查询 + */ +export function queryOne(sql: string, params: any[] = []): Promise { + return new Promise((resolve, reject) => { + db.get(sql, params, (err: any, row: any) => { + if (err) { + reject(err); + } else { + resolve(row); + } + }); + }); +} + +/** + * 执行更新/插入 + */ +export function execute(sql: string, params: any[] = []): Promise<{ lastID: number; changes: number }> { + return new Promise((resolve, reject) => { + db.run(sql, params, function(err: any) { + if (err) { + reject(err); + } else { + resolve({ lastID: (this as any).lastID, changes: (this as any).changes }); + } + }); + }); +} + +/** + * 关闭数据库连接 + */ +export function closeDatabase(): void { + db.close(); +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/auth.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/auth.ts new file mode 100644 index 00000000..fc1f31cf --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/auth.ts @@ -0,0 +1,221 @@ +import { Router, Request, Response } from 'express'; +import * as bcrypt from 'bcryptjs'; +import * as jwt from 'jsonwebtoken'; +import { query, execute } from '../database'; + +export const authRoutes = Router(); + +// JWT密钥 - 生产环境应该从环境变量读取 +const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production'; +const JWT_EXPIRES_IN = '24h'; + +interface LoginRequest { + username: string; + password: string; +} + +interface AuthRequest extends Request { + user?: { + id: number; + username: string; + role: string; + }; +} + +/** + * 登录 + */ +authRoutes.post('/login', async (req: Request, res: Response) => { + try { + const { username, password }: LoginRequest = req.body; + + if (!username || !password) { + return res.status(400).json({ + success: false, + error: '用户名和密码不能为空' + }); + } + + // 查找用户 + const users = await query(` + SELECT id, username, password_hash, role + FROM users + WHERE username = ? + `, [username]); + + if (users.length === 0) { + return res.status(401).json({ + success: false, + error: '用户名或密码错误' + }); + } + + const user = users[0]; + + // 验证密码 + const isPasswordValid = await bcrypt.compare(password, user.password_hash); + + if (!isPasswordValid) { + return res.status(401).json({ + success: false, + error: '用户名或密码错误' + }); + } + + // 生成JWT token + const token = jwt.sign( + { + id: user.id, + username: user.username, + role: user.role + }, + JWT_SECRET, + { expiresIn: JWT_EXPIRES_IN } + ); + + // 更新最后登录时间 + await execute(` + UPDATE users + SET last_login = CURRENT_TIMESTAMP + WHERE id = ? + `, [user.id]); + + res.json({ + success: true, + data: { + token, + user: { + id: user.id, + username: user.username, + role: user.role + } + } + }); + + } catch (error) { + console.error('登录失败:', error); + res.status(500).json({ + success: false, + error: '登录失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 验证token + */ +authRoutes.get('/verify', authenticateToken, (req: AuthRequest, res: Response) => { + res.json({ + success: true, + data: { + user: req.user + } + }); +}); + +/** + * 登出 (清除客户端token即可) + */ +authRoutes.post('/logout', (req: Request, res: Response) => { + res.json({ + success: true, + message: '登出成功' + }); +}); + +/** + * 修改密码 + */ +authRoutes.post('/change-password', authenticateToken, async (req: AuthRequest, res: Response) => { + try { + const { currentPassword, newPassword } = req.body; + + if (!currentPassword || !newPassword) { + return res.status(400).json({ + success: false, + error: '当前密码和新密码不能为空' + }); + } + + if (newPassword.length < 6) { + return res.status(400).json({ + success: false, + error: '新密码长度不能少于6位' + }); + } + + // 获取当前用户信息 + const users = await query(` + SELECT password_hash FROM users WHERE id = ? + `, [req.user!.id]); + + if (users.length === 0) { + return res.status(404).json({ + success: false, + error: '用户不存在' + }); + } + + // 验证当前密码 + const isCurrentPasswordValid = await bcrypt.compare(currentPassword, users[0].password_hash); + + if (!isCurrentPasswordValid) { + return res.status(401).json({ + success: false, + error: '当前密码错误' + }); + } + + // 加密新密码 + const saltRounds = 10; + const hashedNewPassword = await bcrypt.hash(newPassword, saltRounds); + + // 更新密码 + await execute(` + UPDATE users + SET password_hash = ? + WHERE id = ? + `, [hashedNewPassword, req.user!.id]); + + res.json({ + success: true, + message: '密码修改成功' + }); + + } catch (error) { + console.error('修改密码失败:', error); + res.status(500).json({ + success: false, + error: '修改密码失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * JWT Token验证中间件 + */ +export function authenticateToken(req: AuthRequest, res: Response, next: any) { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN + + if (!token) { + return res.status(401).json({ + success: false, + error: '访问令牌缺失' + }); + } + + jwt.verify(token, JWT_SECRET, (err: any, user: any) => { + if (err) { + return res.status(403).json({ + success: false, + error: '访问令牌无效或已过期' + }); + } + + req.user = user; + next(); + }); +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/config.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/config.ts new file mode 100644 index 00000000..d2df4ec2 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/config.ts @@ -0,0 +1,190 @@ +import { Router, Request, Response } from 'express'; +import { query, execute } from '../database'; + +export const configRoutes = Router(); + +/** + * 获取所有配置 + */ +configRoutes.get('/', async (req: Request, res: Response) => { + try { + const configs = await query(` + SELECT key, value, description, updated_at + FROM config + ORDER BY key + `); + + res.json({ + success: true, + data: configs + }); + + } catch (error) { + console.error('获取配置失败:', error); + res.status(500).json({ + success: false, + error: '获取配置失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 获取特定配置 + */ +configRoutes.get('/:key', async (req: Request, res: Response) => { + try { + const { key } = req.params; + + const config = await query(` + SELECT key, value, description, updated_at + FROM config + WHERE key = ? + `, [key]); + + if (config.length === 0) { + return res.status(404).json({ + success: false, + error: '配置不存在' + }); + } + + res.json({ + success: true, + data: config[0] + }); + + } catch (error) { + console.error('获取配置失败:', error); + res.status(500).json({ + success: false, + error: '获取配置失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 更新配置 + */ +configRoutes.put('/:key', async (req: Request, res: Response) => { + try { + const { key } = req.params; + const { value, description } = req.body; + + if (value === undefined) { + return res.status(400).json({ + success: false, + error: '缺少配置值' + }); + } + + // 检查配置是否存在 + const existing = await query('SELECT key FROM config WHERE key = ?', [key]); + + if (existing.length === 0) { + // 创建新配置 + await execute(` + INSERT INTO config (key, value, description, updated_at) + VALUES (?, ?, ?, CURRENT_TIMESTAMP) + `, [key, value, description || '']); + } else { + // 更新现有配置 + await execute(` + UPDATE config + SET value = ?, description = ?, updated_at = CURRENT_TIMESTAMP + WHERE key = ? + `, [value, description || '', key]); + } + + res.json({ + success: true, + message: '配置更新成功' + }); + + } catch (error) { + console.error('更新配置失败:', error); + res.status(500).json({ + success: false, + error: '更新配置失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 删除配置 + */ +configRoutes.delete('/:key', async (req: Request, res: Response) => { + try { + const { key } = req.params; + + await execute('DELETE FROM config WHERE key = ?', [key]); + + res.json({ + success: true, + message: '配置删除成功' + }); + + } catch (error) { + console.error('删除配置失败:', error); + res.status(500).json({ + success: false, + error: '删除配置失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 批量更新配置 + */ +configRoutes.post('/batch', async (req: Request, res: Response) => { + try { + const { configs } = req.body; + + if (!Array.isArray(configs)) { + return res.status(400).json({ + success: false, + error: '配置数据格式错误' + }); + } + + for (const config of configs) { + const { key, value, description } = config; + + if (!key || value === undefined) { + continue; + } + + // 检查配置是否存在 + const existing = await query('SELECT key FROM config WHERE key = ?', [key]); + + if (existing.length === 0) { + await execute(` + INSERT INTO config (key, value, description, updated_at) + VALUES (?, ?, ?, CURRENT_TIMESTAMP) + `, [key, value, description || '']); + } else { + await execute(` + UPDATE config + SET value = ?, description = ?, updated_at = CURRENT_TIMESTAMP + WHERE key = ? + `, [value, description || '', key]); + } + } + + res.json({ + success: true, + message: '批量配置更新成功' + }); + + } catch (error) { + console.error('批量更新配置失败:', error); + res.status(500).json({ + success: false, + error: '批量更新配置失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/plugins.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/plugins.ts new file mode 100644 index 00000000..836f2238 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/plugins.ts @@ -0,0 +1,302 @@ +import { Router, Request, Response } from 'express'; +import { query, queryOne, execute } from '../database'; + +export const pluginRoutes = Router(); + +/** + * 获取插件列表 + */ +pluginRoutes.get('/', async (req: Request, res: Response) => { + try { + const plugins = await query(` + SELECT + p.*, + COUNT(DISTINCT v.id) as version_count, + COALESCE(SUM(us.user_count), 0) as total_downloads, + MAX(v.created_at) as latest_version_date + FROM plugins p + LEFT JOIN versions v ON p.id = v.plugin_id + LEFT JOIN update_stats us ON p.id = us.plugin_id + GROUP BY p.id + ORDER BY p.display_name + `); + + res.json({ + success: true, + data: plugins.map(p => ({ + id: p.id, + name: p.name, + displayName: p.display_name, + description: p.description, + author: p.author, + repository: p.repository, + icon: p.icon, + status: p.status, + versionCount: p.version_count, + totalDownloads: p.total_downloads, + latestVersionDate: p.latest_version_date, + createdAt: p.created_at + })) + }); + + } catch (error) { + console.error('获取插件列表失败:', error); + res.status(500).json({ + success: false, + error: '获取插件列表失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 获取特定插件信息 + */ +pluginRoutes.get('/:id', async (req: Request, res: Response) => { + try { + const { id } = req.params; + + const plugin = await queryOne(` + SELECT + p.*, + COUNT(DISTINCT v.id) as version_count, + COALESCE(SUM(us.user_count), 0) as total_downloads, + MAX(v.created_at) as latest_version_date + FROM plugins p + LEFT JOIN versions v ON p.id = v.plugin_id + LEFT JOIN update_stats us ON p.id = us.plugin_id + WHERE p.id = ? + GROUP BY p.id + `, [id]); + + if (!plugin) { + return res.status(404).json({ + success: false, + error: '插件不存在' + }); + } + + res.json({ + success: true, + data: { + id: plugin.id, + name: plugin.name, + displayName: plugin.display_name, + description: plugin.description, + author: plugin.author, + repository: plugin.repository, + icon: plugin.icon, + status: plugin.status, + versionCount: plugin.version_count, + totalDownloads: plugin.total_downloads, + latestVersionDate: plugin.latest_version_date, + createdAt: plugin.created_at + } + }); + + } catch (error) { + console.error('获取插件信息失败:', error); + res.status(500).json({ + success: false, + error: '获取插件信息失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 创建新插件 + */ +pluginRoutes.post('/', async (req: Request, res: Response) => { + try { + const { id, name, displayName, description, author, repository, icon } = req.body; + + if (!id || !name || !displayName) { + return res.status(400).json({ + success: false, + error: '缺少必要参数: id, name, displayName' + }); + } + + // 检查插件ID是否已存在 + const existingPlugin = await queryOne('SELECT id FROM plugins WHERE id = ?', [id]); + + if (existingPlugin) { + return res.status(400).json({ + success: false, + error: '插件ID已存在' + }); + } + + // 创建插件记录 + await execute(` + INSERT INTO plugins + (id, name, display_name, description, author, repository, icon) + VALUES (?, ?, ?, ?, ?, ?, ?) + `, [id, name, displayName, description || '', author || '', repository || '', icon || '📦']); + + console.log(`✅ 新插件创建成功: ${displayName} (${id})`); + + res.json({ + success: true, + data: { + id, + name, + displayName, + description: description || '', + author: author || '', + repository: repository || '', + icon: icon || '📦', + status: 'active' + }, + message: '插件创建成功' + }); + + } catch (error) { + console.error('创建插件失败:', error); + res.status(500).json({ + success: false, + error: '创建插件失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 更新插件信息 + */ +pluginRoutes.put('/:id', async (req: Request, res: Response) => { + try { + const { id } = req.params; + const { displayName, description, author, repository, icon, status } = req.body; + + // 检查插件是否存在 + const existingPlugin = await queryOne('SELECT id FROM plugins WHERE id = ?', [id]); + + if (!existingPlugin) { + return res.status(404).json({ + success: false, + error: '插件不存在' + }); + } + + // 更新插件信息 + await execute(` + UPDATE plugins + SET display_name = ?, description = ?, author = ?, repository = ?, icon = ?, status = ?, updated_at = ? + WHERE id = ? + `, [displayName, description, author, repository, icon, status, new Date().toISOString(), id]); + + res.json({ + success: true, + message: '插件信息更新成功' + }); + + } catch (error) { + console.error('更新插件失败:', error); + res.status(500).json({ + success: false, + error: '更新插件失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 删除插件(软删除) + */ +pluginRoutes.delete('/:id', async (req: Request, res: Response) => { + try { + const { id } = req.params; + + // 检查插件是否存在 + const existingPlugin = await queryOne('SELECT id FROM plugins WHERE id = ?', [id]); + + if (!existingPlugin) { + return res.status(404).json({ + success: false, + error: '插件不存在' + }); + } + + // 检查是否有版本记录 + const versionCount = await queryOne('SELECT COUNT(*) as count FROM versions WHERE plugin_id = ?', [id]); + + if (versionCount && versionCount.count > 0) { + // 如果有版本记录,只进行软删除 + await execute(` + UPDATE plugins + SET status = 'inactive', updated_at = ? + WHERE id = ? + `, [new Date().toISOString(), id]); + + res.json({ + success: true, + message: '插件已停用(因为存在版本记录)' + }); + } else { + // 如果没有版本记录,可以直接删除 + await execute('DELETE FROM plugins WHERE id = ?', [id]); + + res.json({ + success: true, + message: '插件删除成功' + }); + } + + } catch (error) { + console.error('删除插件失败:', error); + res.status(500).json({ + success: false, + error: '删除插件失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 激活/停用插件 + */ +pluginRoutes.patch('/:id/status', async (req: Request, res: Response) => { + try { + const { id } = req.params; + const { status } = req.body; + + if (!['active', 'inactive'].includes(status)) { + return res.status(400).json({ + success: false, + error: '无效的状态值,必须是 active 或 inactive' + }); + } + + // 检查插件是否存在 + const existingPlugin = await queryOne('SELECT id FROM plugins WHERE id = ?', [id]); + + if (!existingPlugin) { + return res.status(404).json({ + success: false, + error: '插件不存在' + }); + } + + // 更新状态 + await execute(` + UPDATE plugins + SET status = ?, updated_at = ? + WHERE id = ? + `, [status, new Date().toISOString(), id]); + + res.json({ + success: true, + message: `插件已${status === 'active' ? '激活' : '停用'}` + }); + + } catch (error) { + console.error('更新插件状态失败:', error); + res.status(500).json({ + success: false, + error: '更新插件状态失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/stats.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/stats.ts new file mode 100644 index 00000000..eba13923 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/stats.ts @@ -0,0 +1,299 @@ +import { Router, Request, Response } from 'express'; +import { query, queryOne, execute } from '../database'; + +export const statsRoutes = Router(); + +/** + * 获取整体统计信息 + */ +statsRoutes.get('/', async (req: Request, res: Response) => { + try { + // 插件数量统计 + const pluginStats = await query(` + SELECT + COUNT(*) as total_plugins, + COUNT(CASE WHEN status = 'active' THEN 1 END) as active_plugins, + COUNT(CASE WHEN status = 'inactive' THEN 1 END) as inactive_plugins + FROM plugins + `); + + // 版本数量统计 + const versionStats = await query(` + SELECT + COUNT(*) as total_versions, + COUNT(CASE WHEN status = 'active' THEN 1 END) as active_versions, + COUNT(CASE WHEN channel = 'stable' THEN 1 END) as stable_versions, + COUNT(CASE WHEN channel = 'beta' THEN 1 END) as beta_versions, + COUNT(CASE WHEN mandatory = 1 THEN 1 END) as mandatory_versions + FROM versions + `); + + // 文件数量统计 + const fileStats = await query(` + SELECT + COUNT(*) as total_files, + SUM(file_size) as total_size, + AVG(file_size) as avg_file_size + FROM version_files + `); + + // 更新统计 + const updateStats = await query(` + SELECT + SUM(user_count) as total_users, + SUM(success_count) as total_success, + SUM(failure_count) as total_failure + FROM update_stats + `); + + res.json({ + success: true, + data: { + plugins: pluginStats[0] || {}, + versions: versionStats[0] || {}, + files: fileStats[0] || {}, + updates: updateStats[0] || {} + } + }); + + } catch (error) { + console.error('获取统计信息失败:', error); + res.status(500).json({ + success: false, + error: '获取统计信息失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 获取插件使用统计 + */ +statsRoutes.get('/plugins', async (req: Request, res: Response) => { + try { + const { limit = 10 } = req.query; + + const pluginStats = await query(` + SELECT + p.id, + p.display_name, + p.icon, + COUNT(v.id) as version_count, + COALESCE(SUM(us.user_count), 0) as total_users, + COALESCE(SUM(us.success_count), 0) as total_success, + COALESCE(SUM(us.failure_count), 0) as total_failure, + MAX(v.created_at) as latest_version_date + FROM plugins p + LEFT JOIN versions v ON p.id = v.plugin_id AND v.status = 'active' + LEFT JOIN update_stats us ON p.id = us.plugin_id + WHERE p.status = 'active' + GROUP BY p.id, p.display_name, p.icon + ORDER BY total_users DESC + LIMIT ? + `, [Number(limit)]); + + res.json({ + success: true, + data: pluginStats + }); + + } catch (error) { + console.error('获取插件统计失败:', error); + res.status(500).json({ + success: false, + error: '获取插件统计失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 获取版本使用统计 + */ +statsRoutes.get('/versions', async (req: Request, res: Response) => { + try { + const { pluginId, days = 30 } = req.query; + + let sql = ` + SELECT + v.id, + v.version, + v.channel, + v.release_date, + p.display_name as plugin_name, + COALESCE(us.user_count, 0) as user_count, + COALESCE(us.success_count, 0) as success_count, + COALESCE(us.failure_count, 0) as failure_count, + CASE + WHEN us.user_count > 0 + THEN ROUND((us.success_count * 1.0 / us.user_count) * 100, 2) + ELSE 0 + END as success_rate + FROM versions v + LEFT JOIN plugins p ON v.plugin_id = p.id + LEFT JOIN update_stats us ON v.plugin_id = us.plugin_id AND v.version = us.version + WHERE v.status = 'active' + AND v.release_date >= date('now', '-' || ? || ' days') + `; + const params: any[] = [Number(days)]; + + if (pluginId) { + sql += ' AND v.plugin_id = ?'; + params.push(pluginId); + } + + sql += ' ORDER BY v.release_date DESC'; + + const versionStats = await query(sql, params); + + res.json({ + success: true, + data: versionStats + }); + + } catch (error) { + console.error('获取版本统计失败:', error); + res.status(500).json({ + success: false, + error: '获取版本统计失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 获取趋势统计 + */ +statsRoutes.get('/trends', async (req: Request, res: Response) => { + try { + const { period = 'week' } = req.query; + + let dateFormat = '%Y-%m-%d'; + let dateInterval = '7 days'; + + if (period === 'month') { + dateFormat = '%Y-%m'; + dateInterval = '30 days'; + } else if (period === 'year') { + dateFormat = '%Y'; + dateInterval = '365 days'; + } + + // 版本发布趋势 + const versionTrends = await query(` + SELECT + strftime(?, release_date) as period, + COUNT(*) as version_count, + COUNT(CASE WHEN channel = 'stable' THEN 1 END) as stable_count, + COUNT(CASE WHEN channel = 'beta' THEN 1 END) as beta_count + FROM versions + WHERE release_date >= date('now', '-' || ? || '') + AND status = 'active' + GROUP BY strftime(?, release_date) + ORDER BY period DESC + LIMIT 20 + `, [dateFormat, dateInterval, dateFormat]); + + // 更新使用趋势 + const updateTrends = await query(` + SELECT + strftime(?, last_updated) as period, + SUM(user_count) as total_users, + SUM(success_count) as total_success, + SUM(failure_count) as total_failure + FROM update_stats + WHERE last_updated >= date('now', '-' || ? || '') + GROUP BY strftime(?, last_updated) + ORDER BY period DESC + LIMIT 20 + `, [dateFormat, dateInterval, dateFormat]); + + res.json({ + success: true, + data: { + versions: versionTrends, + updates: updateTrends + } + }); + + } catch (error) { + console.error('获取趋势统计失败:', error); + res.status(500).json({ + success: false, + error: '获取趋势统计失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 记录更新成功 + */ +statsRoutes.post('/success', async (req: Request, res: Response) => { + try { + const { pluginId, version } = req.body; + + if (!pluginId || !version) { + return res.status(400).json({ + success: false, + error: '缺少必要参数: pluginId, version' + }); + } + + await execute(` + UPDATE update_stats + SET success_count = success_count + 1, last_updated = ? + WHERE plugin_id = ? AND version = ? + `, [new Date().toISOString(), pluginId, version]); + + res.json({ + success: true, + message: '更新成功记录已保存' + }); + + } catch (error) { + console.error('记录更新成功失败:', error); + res.status(500).json({ + success: false, + error: '记录更新成功失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 记录更新失败 + */ +statsRoutes.post('/failure', async (req: Request, res: Response) => { + try { + const { pluginId, version, error: errorMessage } = req.body; + + if (!pluginId || !version) { + return res.status(400).json({ + success: false, + error: '缺少必要参数: pluginId, version' + }); + } + + await execute(` + UPDATE update_stats + SET failure_count = failure_count + 1, last_updated = ? + WHERE plugin_id = ? AND version = ? + `, [new Date().toISOString(), pluginId, version]); + + console.log(`❌ 更新失败记录: ${pluginId} v${version} - ${errorMessage}`); + + res.json({ + success: true, + message: '更新失败记录已保存' + }); + + } catch (error) { + console.error('记录更新失败失败:', error); + res.status(500).json({ + success: false, + error: '记录更新失败失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/upload.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/upload.ts new file mode 100644 index 00000000..e7cd1dbd --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/upload.ts @@ -0,0 +1,251 @@ +import { Router, Request, Response } from 'express'; +import * as multer from 'multer'; +import * as path from 'path'; +import * as fs from 'fs-extra'; +import * as crypto from 'crypto'; +import * as AdmZip from 'adm-zip'; +import { execute, query } from '../database'; + +interface MulterRequest extends Request { + file?: any; +} + +export const uploadRoutes = Router(); + +// 配置multer用于文件上传 +const storage = multer.diskStorage({ + destination: async (req, file, cb) => { + const uploadDir = path.join(__dirname, '../../uploads/packages'); + await fs.ensureDir(uploadDir); + cb(null, uploadDir); + }, + filename: (req, file, cb) => { + // 生成唯一文件名 + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + const ext = path.extname(file.originalname); + cb(null, `${file.fieldname}-${uniqueSuffix}${ext}`); + } +}); + +const upload = multer({ + storage: storage, + limits: { + fileSize: 100 * 1024 * 1024 // 100MB限制 + }, + fileFilter: (req, file, cb) => { + if (file.mimetype === 'application/zip' || path.extname(file.originalname).toLowerCase() === '.zip') { + cb(null, true); + } else { + cb(new Error('只允许上传ZIP文件')); + } + } +}); + +/** + * 上传插件包 + */ +uploadRoutes.post('/package', upload.single('package'), async (req: MulterRequest, res: Response) => { + try { + if (!req.file) { + return res.status(400).json({ + success: false, + error: '未选择文件' + }); + } + + const { pluginId, version, channel, description, mandatory } = req.body; + + if (!pluginId || !version || !channel) { + // 删除已上传的文件 + await fs.remove(req.file.path); + return res.status(400).json({ + success: false, + error: '缺少必要参数: pluginId, version, channel' + }); + } + + console.log(`📦 上传插件包: ${pluginId} v${version} (${channel})`); + + // 计算文件哈希 + const fileHash = await calculateFileHash(req.file.path); + + // 分析ZIP文件内容 + const zipAnalysis = await analyzeZipFile(req.file.path); + + // 生成下载URL + const downloadUrl = `/uploads/packages/${req.file.filename}`; + + // 创建版本记录 + const result = await execute(` + INSERT INTO versions + (plugin_id, version, channel, description, download_url, file_size, checksum, mandatory, release_date) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + `, [ + pluginId, + version, + channel, + description || '', + downloadUrl, + req.file.size, + fileHash, + mandatory === 'true' ? 1 : 0, + new Date().toISOString() + ]); + + const versionId = result.lastID; + + // 添加文件记录 + for (const file of zipAnalysis.files) { + await execute(` + INSERT INTO version_files (version_id, file_path, file_hash, file_size, action) + VALUES (?, ?, ?, ?, ?) + `, [versionId, file.path, file.hash, file.size, 'update']); + } + + res.json({ + success: true, + data: { + id: versionId, + pluginId, + version, + channel, + description: description || '', + downloadUrl, + fileSize: req.file.size, + checksum: fileHash, + mandatory: mandatory === 'true', + filesCount: zipAnalysis.files.length, + uploadedAt: new Date().toISOString() + } + }); + + } catch (error) { + // 清理文件 + if (req.file) { + await fs.remove(req.file.path).catch(() => {}); + } + + console.error('上传失败:', error); + res.status(500).json({ + success: false, + error: '上传失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 上传进度查询 + */ +uploadRoutes.get('/progress/:id', async (req: Request, res: Response) => { + try { + const { id } = req.params; + + // 这里可以实现真正的进度追踪 + // 目前返回完成状态 + res.json({ + success: true, + data: { + id, + progress: 100, + status: 'completed' + } + }); + + } catch (error) { + console.error('查询上传进度失败:', error); + res.status(500).json({ + success: false, + error: '查询上传进度失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 获取上传历史 + */ +uploadRoutes.get('/history', async (req: Request, res: Response) => { + try { + const { pluginId, limit = 10 } = req.query; + + let sql = ` + SELECT v.*, p.display_name as plugin_display_name, p.icon as plugin_icon + FROM versions v + LEFT JOIN plugins p ON v.plugin_id = p.id + WHERE 1=1 + `; + const params: any[] = []; + + if (pluginId) { + sql += ' AND v.plugin_id = ?'; + params.push(pluginId); + } + + sql += ` ORDER BY v.created_at DESC LIMIT ?`; + params.push(Number(limit)); + + const uploads = await query(sql, params); + + res.json({ + success: true, + data: uploads.map(u => ({ + id: u.id, + pluginId: u.plugin_id, + version: u.version, + channel: u.channel, + fileSize: u.file_size, + uploadedAt: u.created_at, + pluginDisplayName: u.plugin_display_name, + pluginIcon: u.plugin_icon + })) + }); + + } catch (error) { + console.error('获取上传历史失败:', error); + res.status(500).json({ + success: false, + error: '获取上传历史失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 计算文件哈希值 + */ +async function calculateFileHash(filePath: string): Promise { + return new Promise((resolve, reject) => { + const hash = crypto.createHash('sha256'); + const stream = fs.createReadStream(filePath); + + stream.on('data', data => hash.update(data)); + stream.on('end', () => resolve(hash.digest('hex'))); + stream.on('error', reject); + }); +} + +/** + * 分析ZIP文件内容 + */ +async function analyzeZipFile(filePath: string): Promise<{ files: Array<{ path: string; hash: string; size: number }> }> { + const zip = new AdmZip(filePath); + const entries = zip.getEntries(); + + const files: Array<{ path: string; hash: string; size: number }> = []; + + for (const entry of entries) { + if (!entry.isDirectory) { + const content = entry.getData(); + const hash = crypto.createHash('sha256').update(content).digest('hex'); + + files.push({ + path: entry.entryName, + hash: hash, + size: entry.header.size + }); + } + } + + return { files }; +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/versions.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/versions.ts new file mode 100644 index 00000000..c9c7ef78 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/routes/versions.ts @@ -0,0 +1,348 @@ +import { Router, Request, Response } from 'express'; +import { query, queryOne, execute } from '../database'; + +export const versionRoutes = Router(); + +/** + * 获取所有版本列表 + */ +versionRoutes.get('/', async (req: Request, res: Response) => { + try { + const { pluginId, channel, limit = 50 } = req.query; + + let sql = ` + SELECT v.*, p.display_name as plugin_display_name, p.icon as plugin_icon + FROM versions v + LEFT JOIN plugins p ON v.plugin_id = p.id + WHERE v.status = 'active' + `; + const params: any[] = []; + + if (pluginId) { + sql += ' AND v.plugin_id = ?'; + params.push(pluginId); + } + + if (channel) { + sql += ' AND v.channel = ?'; + params.push(channel); + } + + sql += ` ORDER BY v.created_at DESC LIMIT ?`; + params.push(Number(limit)); + + const versions = await query(sql, params); + + res.json({ + success: true, + data: versions + }); + + } catch (error) { + console.error('获取版本列表失败:', error); + res.status(500).json({ + success: false, + error: '获取版本列表失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 获取特定版本详情 + */ +versionRoutes.get('/:id', async (req: Request, res: Response) => { + try { + const { id } = req.params; + + const version = await query(` + SELECT v.*, p.display_name as plugin_display_name, p.icon as plugin_icon + FROM versions v + LEFT JOIN plugins p ON v.plugin_id = p.id + WHERE v.id = ? AND v.status = 'active' + `, [id]); + + if (version.length === 0) { + return res.status(404).json({ + success: false, + error: '版本不存在' + }); + } + + // 获取版本文件列表 + const files = await query(` + SELECT * FROM version_files WHERE version_id = ? + `, [id]); + + res.json({ + success: true, + data: { + ...version[0], + files + } + }); + + } catch (error) { + console.error('获取版本详情失败:', error); + res.status(500).json({ + success: false, + error: '获取版本详情失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 删除版本 + */ +versionRoutes.delete('/:id', async (req: Request, res: Response) => { + try { + const { id } = req.params; + + // 软删除 + await execute(` + UPDATE versions SET status = 'deleted', updated_at = CURRENT_TIMESTAMP + WHERE id = ? + `, [id]); + + res.json({ + success: true, + message: '版本删除成功' + }); + + } catch (error) { + console.error('删除版本失败:', error); + res.status(500).json({ + success: false, + error: '删除版本失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 更新版本状态 + */ +versionRoutes.patch('/:id/status', async (req: Request, res: Response) => { + try { + const { id } = req.params; + const { status } = req.body; + + if (!['active', 'inactive', 'deleted'].includes(status)) { + return res.status(400).json({ + success: false, + error: '无效的状态值' + }); + } + + await execute(` + UPDATE versions SET status = ?, updated_at = CURRENT_TIMESTAMP + WHERE id = ? + `, [status, id]); + + res.json({ + success: true, + message: '状态更新成功' + }); + + } catch (error) { + console.error('更新版本状态失败:', error); + res.status(500).json({ + success: false, + error: '更新版本状态失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 检查更新 (供客户端使用) + */ +versionRoutes.post('/check', async (req: Request, res: Response) => { + try { + const { currentVersion, pluginId = 'cocos-ecs-extension', channel = 'stable', platform, editorVersion } = req.body; + + console.log(`📱 插件更新检查: ${pluginId} v${currentVersion} (${channel}) - ${platform}`); + + // 查找最新版本 + const latestVersion = await queryOne(` + SELECT * FROM versions + WHERE plugin_id = ? AND channel = ? AND status = 'active' + ORDER BY created_at DESC + LIMIT 1 + `, [pluginId, channel]); + + if (!latestVersion) { + return res.json({ + hasUpdate: false, + message: `暂无 ${pluginId} 的 ${channel} 版本`, + currentVersion + }); + } + + // 比较版本号 + const hasUpdate = isNewerVersion(latestVersion.version, currentVersion); + + if (hasUpdate) { + // 记录更新检查 + await execute(` + INSERT OR REPLACE INTO update_stats (plugin_id, version, user_count, last_updated) + VALUES (?, ?, COALESCE((SELECT user_count FROM update_stats WHERE plugin_id = ? AND version = ?), 0) + 1, ?) + `, [pluginId, latestVersion.version, pluginId, latestVersion.version, new Date().toISOString()]); + + // 获取文件列表 + const files = await query(` + SELECT file_path, file_hash, file_size, action + FROM version_files + WHERE version_id = ? + `, [latestVersion.id]); + + res.json({ + hasUpdate: true, + version: latestVersion.version, + releaseDate: latestVersion.release_date, + description: latestVersion.description, + downloadUrl: latestVersion.download_url, + fileSize: latestVersion.file_size, + checksum: latestVersion.checksum, + mandatory: latestVersion.mandatory === 1, + files: files.map(f => ({ + path: f.file_path, + hash: f.file_hash, + size: f.file_size, + action: f.action + })) + }); + } else { + res.json({ + hasUpdate: false, + message: `${pluginId} 当前已是最新版本`, + currentVersion + }); + } + + } catch (error) { + console.error('检查更新失败:', error); + res.status(500).json({ + hasUpdate: false, + error: '检查更新失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 创建新版本 + */ +versionRoutes.post('/', async (req: Request, res: Response) => { + try { + const { pluginId, version, channel, description, downloadUrl, fileSize, checksum, mandatory, files } = req.body; + + // 检查版本是否已存在 + const existingVersion = await queryOne(` + SELECT id FROM versions + WHERE plugin_id = ? AND version = ? AND channel = ? + `, [pluginId, version, channel]); + + if (existingVersion) { + return res.status(400).json({ + success: false, + error: '该版本已存在' + }); + } + + // 创建版本记录 + const result = await execute(` + INSERT INTO versions + (plugin_id, version, channel, description, download_url, file_size, checksum, mandatory, release_date) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + `, [pluginId, version, channel, description, downloadUrl, fileSize, checksum, mandatory ? 1 : 0, new Date().toISOString()]); + + const versionId = result.lastID; + + // 添加文件记录 + if (files && files.length > 0) { + for (const file of files) { + await execute(` + INSERT INTO version_files (version_id, file_path, file_hash, file_size, action) + VALUES (?, ?, ?, ?, ?) + `, [versionId, file.path, file.hash, file.size, file.action || 'update']); + } + } + + res.json({ + success: true, + data: { + id: versionId, + pluginId, + version, + channel, + description, + downloadUrl, + fileSize, + checksum, + mandatory + } + }); + + } catch (error) { + console.error('创建版本失败:', error); + res.status(500).json({ + success: false, + error: '创建版本失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 更新版本信息 + */ +versionRoutes.put('/:id', async (req: Request, res: Response) => { + try { + const { id } = req.params; + const { description, status, mandatory } = req.body; + + await execute(` + UPDATE versions + SET description = ?, status = ?, mandatory = ?, updated_at = ? + WHERE id = ? + `, [description, status, mandatory ? 1 : 0, new Date().toISOString(), id]); + + res.json({ + success: true, + message: '版本信息更新成功' + }); + + } catch (error) { + console.error('更新版本失败:', error); + res.status(500).json({ + success: false, + error: '更新版本失败', + message: error instanceof Error ? error.message : String(error) + }); + } +}); + +/** + * 比较版本号 + */ +function isNewerVersion(newVersion: string, currentVersion: string): boolean { + const parseVersion = (version: string) => { + return version.split('.').map(Number); + }; + + const newParts = parseVersion(newVersion); + const currentParts = parseVersion(currentVersion); + const maxLength = Math.max(newParts.length, currentParts.length); + + for (let i = 0; i < maxLength; i++) { + const newPart = newParts[i] || 0; + const currentPart = currentParts[i] || 0; + + if (newPart > currentPart) return true; + if (newPart < currentPart) return false; + } + + return false; +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/server.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/server.ts new file mode 100644 index 00000000..ee832c66 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/src/server.ts @@ -0,0 +1,91 @@ +import * as express from 'express'; +import { Request, Response, NextFunction } from 'express'; +import * as cors from 'cors'; +import * as path from 'path'; +import { authRoutes } from './routes/auth'; +import { uploadRoutes } from './routes/upload'; +import { versionRoutes } from './routes/versions'; +import { configRoutes } from './routes/config'; +import { statsRoutes } from './routes/stats'; +import { initDatabase } from './database'; + +const app = express(); +const PORT = process.env.PORT || 3001; + +// 中间件 +app.use(cors()); +app.use(express.json({ limit: '100mb' })); +app.use(express.urlencoded({ extended: true, limit: '100mb' })); + +// 静态文件服务 +app.use('/uploads', express.static(path.join(__dirname, '../uploads'))); +app.use('/assets', express.static(path.join(__dirname, '../public'))); + +// API路由 +app.use('/api/auth', authRoutes); +app.use('/api/upload', uploadRoutes); +app.use('/api/config', configRoutes); +app.use('/api/stats', statsRoutes); + +// 热更新客户端API (供Cocos Creator插件使用) +app.post('/api/plugin-updates/check', (req: Request, res: Response) => { + // 检查插件更新 + res.json({ + message: '当前已是最新版本', + hasUpdate: false, + currentVersion: req.body?.currentVersion || '1.0.0' + }); +}); + +// 管理界面路由 +app.get('/', (req: Request, res: Response) => { + res.sendFile(path.join(__dirname, '../public/index.html')); +}); + +app.get('/admin', (req: Request, res: Response) => { + res.sendFile(path.join(__dirname, '../public/index.html')); +}); + +// 健康检查 +app.get('/health', (req: Request, res: Response) => { + res.json({ + status: 'ok', + timestamp: new Date().toISOString(), + version: '1.0.0' + }); +}); + +// 错误处理中间件 +app.use((err: any, req: Request, res: Response, next: NextFunction) => { + console.error('Error:', err); + res.status(500).json({ + error: '服务器内部错误', + message: process.env.NODE_ENV === 'development' ? err.message : '请联系管理员' + }); +}); + +// 404处理 +app.use('*', (req: Request, res: Response) => { + res.status(404).json({ error: '接口不存在' }); +}); + +// 启动服务器 +async function startServer() { + try { + // 初始化数据库 + await initDatabase(); + + app.listen(PORT, () => { + console.log(`🚀 热更新管理后台启动成功!`); + console.log(`📍 服务地址: http://localhost:${PORT}`); + console.log(`📱 管理界面: http://localhost:${PORT}`); + console.log(`📱 管理界面(admin): http://localhost:${PORT}/admin`); + console.log(`🔗 健康检查: http://localhost:${PORT}/health`); + }); + } catch (error) { + console.error('❌ 服务器启动失败:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/tsconfig.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/tsconfig.json new file mode 100644 index 00000000..bd3b9515 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/admin-backend/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "moduleResolution": "node", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist", + "uploads" + ] +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package.json index 8e6d7679..3767a492 100644 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package.json +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/package.json @@ -14,13 +14,15 @@ "dependencies": { "vue": "^3.1.4", "fs-extra": "^10.0.0", - "ws": "^8.14.2" + "ws": "^8.14.2", + "adm-zip": "^0.5.10" }, "devDependencies": { "@cocos/creator-types": "^3.8.6", "@types/fs-extra": "^9.0.5", "@types/node": "^18.17.1", "@types/ws": "^8.5.10", + "@types/adm-zip": "^0.5.0", "typescript": "^5.8.2" }, "panels": { @@ -98,6 +100,11 @@ "path": "i18n:menu.develop/ECS Framework", "label": "ECS 开发工具", "message": "open-panel" + }, + { + "path": "i18n:menu.panel/ECS Framework", + "label": "检查更新", + "message": "check-plugin-updates" } ], "assets": { @@ -177,6 +184,21 @@ "check-behavior-tree-installed" ] }, + "check-plugin-updates": { + "methods": [ + "check-plugin-updates" + ] + }, + "set-hot-update-config": { + "methods": [ + "set-hot-update-config" + ] + }, + "get-hot-update-config": { + "methods": [ + "get-hot-update-config" + ] + }, "open-behavior-tree-docs": { "methods": [ "open-behavior-tree-docs" diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/HotUpdateHandler.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/HotUpdateHandler.ts new file mode 100644 index 00000000..8bd571a5 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/HotUpdateHandler.ts @@ -0,0 +1,571 @@ +import * as fs from 'fs-extra'; +import * as path from 'path'; +import * as crypto from 'crypto'; +import { exec } from 'child_process'; + +/** + * 热更新配置接口 + */ +interface HotUpdateConfig { + serverUrl: string; + currentVersion: string; + updateChannel: 'stable' | 'beta' | 'dev'; + autoCheck: boolean; + checkInterval: number; // 分钟 +} + +/** + * 版本信息接口 + */ +interface VersionInfo { + version: string; + releaseDate: string; + description: string; + downloadUrl: string; + fileSize: number; + checksum: string; + mandatory: boolean; // 是否强制更新 + files: UpdateFile[]; +} + +/** + * 更新文件接口 + */ +interface UpdateFile { + path: string; + hash: string; + size: number; + action: 'add' | 'update' | 'delete'; +} + +/** + * 热更新处理器 + */ +export class HotUpdateHandler { + private static readonly CONFIG_FILE = 'hot-update-config.json'; + private static readonly VERSION_FILE = 'version-info.json'; + private static readonly EXTENSION_PATH = Editor.Package.getPath('cocos-ecs-extension') || ''; + + private static config: HotUpdateConfig; + private static updateTimer: NodeJS.Timeout | null = null; + + /** + * 初始化热更新系统 + */ + static async initialize(): Promise { + console.log('[HotUpdate] 初始化热更新系统...'); + + try { + await this.loadConfig(); + await this.startAutoCheck(); + console.log('[HotUpdate] 热更新系统初始化完成'); + } catch (error) { + console.error('[HotUpdate] 初始化失败:', error); + } + } + + /** + * 加载配置 + */ + private static async loadConfig(): Promise { + const configPath = path.join(this.EXTENSION_PATH, this.CONFIG_FILE); + + try { + if (await fs.pathExists(configPath)) { + this.config = await fs.readJSON(configPath); + } else { + // 创建默认配置 + this.config = { + serverUrl: 'https://earthonline-game.cn/api/plugin-updates', + currentVersion: this.getCurrentVersion(), + updateChannel: 'stable', + autoCheck: true, + checkInterval: 60 // 60分钟检查一次 + }; + await this.saveConfig(); + } + } catch (error) { + console.error('[HotUpdate] 配置加载失败:', error); + throw error; + } + } + + /** + * 保存配置 + */ + private static async saveConfig(): Promise { + const configPath = path.join(this.EXTENSION_PATH, this.CONFIG_FILE); + await fs.writeJSON(configPath, this.config, { spaces: 2 }); + } + + /** + * 获取当前版本 + */ + private static getCurrentVersion(): string { + try { + const packagePath = path.join(this.EXTENSION_PATH, 'package.json'); + const packageInfo = fs.readJSONSync(packagePath); + return packageInfo.version; + } catch (error) { + console.error('[HotUpdate] 无法获取当前版本:', error); + return '1.0.0'; + } + } + + /** + * 开始自动检查 + */ + private static async startAutoCheck(): Promise { + if (!this.config.autoCheck) { + return; + } + + // 立即检查一次 + await this.checkForUpdates(true); + + // 设置定时检查 + if (this.updateTimer) { + clearInterval(this.updateTimer); + } + + this.updateTimer = setInterval(async () => { + await this.checkForUpdates(true); + }, this.config.checkInterval * 60 * 1000); + } + + /** + * 检查更新 + */ + static async checkForUpdates(silent: boolean = false): Promise { + console.log('[HotUpdate] 检查更新中...'); + + try { + const response = await fetch(`${this.config.serverUrl}/check`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + currentVersion: this.config.currentVersion, + pluginId: 'cocos-ecs-extension', // 当前插件ID + channel: this.config.updateChannel, + platform: process.platform, + editorVersion: Editor.App.version + }) + }); + + if (!response.ok) { + throw new Error(`服务器响应错误: ${response.status}`); + } + + const versionInfo: VersionInfo = await response.json(); + + if (this.isNewerVersion(versionInfo.version, this.config.currentVersion)) { + console.log(`[HotUpdate] 发现新版本: ${versionInfo.version}`); + + if (!silent) { + await this.showUpdateDialog(versionInfo); + } + + return versionInfo; + } else { + if (!silent) { + Editor.Dialog.info('检查更新', { + detail: '当前已是最新版本!' + }); + } + return null; + } + } catch (error) { + console.error('[HotUpdate] 检查更新失败:', error); + + if (!silent) { + Editor.Dialog.error('检查更新失败', { + detail: `无法连接到更新服务器:\n\n${error instanceof Error ? error.message : String(error)}` + }); + } + return null; + } + } + + /** + * 比较版本号 + */ + private static isNewerVersion(newVersion: string, currentVersion: string): boolean { + const parseVersion = (version: string) => { + return version.split('.').map(Number); + }; + + const newParts = parseVersion(newVersion); + const currentParts = parseVersion(currentVersion); + const maxLength = Math.max(newParts.length, currentParts.length); + + for (let i = 0; i < maxLength; i++) { + const newPart = newParts[i] || 0; + const currentPart = currentParts[i] || 0; + + if (newPart > currentPart) return true; + if (newPart < currentPart) return false; + } + + return false; + } + + /** + * 显示更新对话框 + */ + private static async showUpdateDialog(versionInfo: VersionInfo): Promise { + const message = `发现新版本 ${versionInfo.version}!\n\n` + + `发布时间: ${versionInfo.releaseDate}\n\n` + + `更新内容:\n${versionInfo.description}\n\n` + + `文件大小: ${this.formatFileSize(versionInfo.fileSize)}`; + + const buttons = versionInfo.mandatory ? ['立即更新'] : ['立即更新', '稍后提醒', '跳过此版本']; + + const result = await Editor.Dialog.info('插件更新', { + detail: message, + buttons: buttons + }); + + switch (result.response) { + case 0: // 立即更新 + await this.downloadAndInstallUpdate(versionInfo); + break; + case 1: // 稍后提醒 + if (!versionInfo.mandatory) { + console.log('[HotUpdate] 用户选择稍后更新'); + } + break; + case 2: // 跳过此版本 + if (!versionInfo.mandatory) { + await this.skipVersion(versionInfo.version); + } + break; + } + } + + /** + * 下载并安装更新 + */ + private static async downloadAndInstallUpdate(versionInfo: VersionInfo): Promise { + console.log(`[HotUpdate] 开始下载更新: ${versionInfo.version}`); + + try { + // 显示进度对话框 + const progressDialog = this.showProgressDialog('正在下载更新...'); + + // 下载更新包 + const updatePath = await this.downloadUpdate(versionInfo, (progress) => { + // 更新进度 + console.log(`[HotUpdate] 下载进度: ${progress}%`); + }); + + progressDialog.detail = '正在验证文件...'; + + // 验证文件完整性 + const isValid = await this.verifyUpdate(updatePath, versionInfo.checksum); + if (!isValid) { + throw new Error('文件校验失败,更新包可能已损坏'); + } + + progressDialog.detail = '正在安装更新...'; + + // 安装更新 + await this.installUpdate(updatePath, versionInfo); + + // 更新版本信息 + this.config.currentVersion = versionInfo.version; + await this.saveConfig(); + + // 显示安装完成对话框 + const result = await Editor.Dialog.info('更新完成', { + detail: `插件已成功更新到版本 ${versionInfo.version}!\n\n为了使更新生效,需要重启Cocos Creator编辑器。`, + buttons: ['立即重启', '稍后重启'] + }); + + if (result.response === 0) { + this.restartEditor(); + } + + } catch (error) { + console.error('[HotUpdate] 更新失败:', error); + Editor.Dialog.error('更新失败', { + detail: `更新过程中发生错误:\n\n${error instanceof Error ? error.message : String(error)}` + }); + } + } + + /** + * 下载更新 + */ + private static async downloadUpdate(versionInfo: VersionInfo, onProgress?: (progress: number) => void): Promise { + const response = await fetch(versionInfo.downloadUrl); + + if (!response.ok) { + throw new Error(`下载失败: ${response.status}`); + } + + const totalSize = parseInt(response.headers.get('content-length') || '0'); + let downloadedSize = 0; + + const tempPath = path.join(this.EXTENSION_PATH, 'temp', `update-${versionInfo.version}.zip`); + await fs.ensureDir(path.dirname(tempPath)); + + const writer = fs.createWriteStream(tempPath); + const reader = response.body?.getReader(); + + if (!reader) { + throw new Error('无法创建下载流'); + } + + try { + while (true) { + const { done, value } = await reader.read(); + + if (done) break; + + writer.write(value); + downloadedSize += value.length; + + if (onProgress && totalSize > 0) { + const progress = Math.round((downloadedSize / totalSize) * 100); + onProgress(progress); + } + } + + writer.end(); + return tempPath; + + } catch (error) { + writer.destroy(); + await fs.remove(tempPath).catch(() => {}); // 忽略删除错误 + throw error; + } + } + + /** + * 验证更新包 + */ + private static async verifyUpdate(filePath: string, expectedChecksum: string): Promise { + try { + const fileBuffer = await fs.readFile(filePath); + const hash = crypto.createHash('sha256'); + hash.update(fileBuffer); + const actualChecksum = hash.digest('hex'); + + return actualChecksum === expectedChecksum; + } catch (error) { + console.error('[HotUpdate] 文件校验失败:', error); + return false; + } + } + + /** + * 安装更新 + */ + private static async installUpdate(updatePath: string, versionInfo: VersionInfo): Promise { + const extractPath = path.join(this.EXTENSION_PATH, 'temp', 'extract'); + + try { + // 清理临时目录 + await fs.remove(extractPath); + await fs.ensureDir(extractPath); + + // 解压更新包 + await this.extractZip(updatePath, extractPath); + + // 备份当前版本 + const backupPath = path.join(this.EXTENSION_PATH, 'backup', this.config.currentVersion); + await this.createBackup(backupPath); + + // 应用更新文件 + await this.applyUpdateFiles(extractPath, versionInfo.files); + + // 清理临时文件 + await fs.remove(path.dirname(updatePath)); + + } catch (error) { + console.error('[HotUpdate] 安装更新失败:', error); + // 尝试恢复备份 + await this.restoreBackup(); + throw error; + } + } + + /** + * 解压ZIP文件 + */ + private static async extractZip(zipPath: string, extractPath: string): Promise { + return new Promise((resolve, reject) => { + // 这里使用node的解压库,您可能需要安装 yauzl 或 adm-zip + const AdmZip = require('adm-zip'); + + try { + const zip = new AdmZip(zipPath); + zip.extractAllTo(extractPath, true); + resolve(); + } catch (error) { + reject(error); + } + }); + } + + /** + * 应用更新文件 + */ + private static async applyUpdateFiles(extractPath: string, files: UpdateFile[]): Promise { + for (const file of files) { + const sourcePath = path.join(extractPath, file.path); + const targetPath = path.join(this.EXTENSION_PATH, file.path); + + try { + switch (file.action) { + case 'add': + case 'update': + await fs.ensureDir(path.dirname(targetPath)); + await fs.copy(sourcePath, targetPath, { overwrite: true }); + break; + case 'delete': + await fs.remove(targetPath); + break; + } + } catch (error) { + console.error(`[HotUpdate] 处理文件失败 ${file.path}:`, error); + throw error; + } + } + } + + /** + * 创建备份 + */ + private static async createBackup(backupPath: string): Promise { + await fs.ensureDir(backupPath); + + const sourceFiles = [ + 'source', + 'static', + 'package.json', + 'README.md' + ]; + + for (const file of sourceFiles) { + const sourcePath = path.join(this.EXTENSION_PATH, file); + const targetPath = path.join(backupPath, file); + + if (await fs.pathExists(sourcePath)) { + await fs.copy(sourcePath, targetPath); + } + } + } + + /** + * 恢复备份 + */ + private static async restoreBackup(): Promise { + const backupPath = path.join(this.EXTENSION_PATH, 'backup', this.config.currentVersion); + + if (await fs.pathExists(backupPath)) { + console.log('[HotUpdate] 正在恢复备份...'); + + const backupFiles = await fs.readdir(backupPath); + for (const file of backupFiles) { + const sourcePath = path.join(backupPath, file); + const targetPath = path.join(this.EXTENSION_PATH, file); + + await fs.copy(sourcePath, targetPath, { overwrite: true }); + } + } + } + + /** + * 跳过版本 + */ + private static async skipVersion(version: string): Promise { + const skipPath = path.join(this.EXTENSION_PATH, 'skipped-versions.json'); + let skippedVersions: string[] = []; + + if (await fs.pathExists(skipPath)) { + skippedVersions = await fs.readJSON(skipPath); + } + + if (!skippedVersions.includes(version)) { + skippedVersions.push(version); + await fs.writeJSON(skipPath, skippedVersions); + } + } + + /** + * 显示进度对话框 + */ + private static showProgressDialog(message: string) { + // 这是一个简化版本,实际可能需要创建自定义进度条 + return { + detail: message + }; + } + + /** + * 格式化文件大小 + */ + private static formatFileSize(bytes: number): string { + const units = ['B', 'KB', 'MB', 'GB']; + let size = bytes; + let unitIndex = 0; + + while (size >= 1024 && unitIndex < units.length - 1) { + size /= 1024; + unitIndex++; + } + + return `${size.toFixed(1)} ${units[unitIndex]}`; + } + + /** + * 重启编辑器 + */ + private static restartEditor(): void { + // 注意:这个功能需要特殊权限,可能需要用户手动重启 + console.log('[HotUpdate] 请求重启编辑器'); + + try { + // 尝试重启编辑器(可能不会成功) + Editor.App.quit(); + } catch (error) { + console.warn('[HotUpdate] 无法自动重启编辑器:', error); + } + } + + /** + * 设置更新配置 + */ + static async setConfig(newConfig: Partial): Promise { + this.config = { ...this.config, ...newConfig }; + await this.saveConfig(); + + // 重新启动自动检查 + if (this.config.autoCheck) { + await this.startAutoCheck(); + } else if (this.updateTimer) { + clearInterval(this.updateTimer); + this.updateTimer = null; + } + } + + /** + * 获取配置 + */ + static getConfig(): HotUpdateConfig { + return { ...this.config }; + } + + /** + * 清理资源 + */ + static cleanup(): void { + if (this.updateTimer) { + clearInterval(this.updateTimer); + this.updateTimer = null; + } + } +} \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/index.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/index.ts index 35c1a57e..646b8c3f 100644 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/index.ts +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/handlers/index.ts @@ -1,3 +1,4 @@ export { EcsFrameworkHandler } from './EcsFrameworkHandler'; export { BehaviorTreeHandler } from './BehaviorTreeHandler'; -export { PanelHandler } from './PanelHandler'; \ No newline at end of file +export { PanelHandler } from './PanelHandler'; +export { HotUpdateHandler } from './HotUpdateHandler'; \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/main.ts b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/main.ts index 4256b411..1f0d2a80 100644 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/main.ts +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/source/main.ts @@ -1,6 +1,6 @@ // @ts-ignore import packageJSON from '../package.json'; -import { EcsFrameworkHandler, BehaviorTreeHandler, PanelHandler } from './handlers'; +import { EcsFrameworkHandler, BehaviorTreeHandler, PanelHandler, HotUpdateHandler } from './handlers'; import { readJSON } from 'fs-extra'; import * as path from 'path'; import { AssetInfo } from '@cocos/creator-types/editor/packages/asset-db/@types/public'; @@ -177,6 +177,29 @@ export const methods: { [key: string]: (...any: any) => any } = { throw new Error('文件路径不存在或数据无效'); } }, + + // ================ 热更新管理 ================ + /** + * 检查插件更新 + */ + 'check-plugin-updates'() { + return HotUpdateHandler.checkForUpdates(false); + }, + + /** + * 设置热更新配置 + */ + 'set-hot-update-config'(...args: any[]) { + const config = args.length >= 2 ? args[1] : args[0]; + return HotUpdateHandler.setConfig(config); + }, + + /** + * 获取热更新配置 + */ + 'get-hot-update-config'() { + return HotUpdateHandler.getConfig(); + }, }; @@ -187,6 +210,11 @@ export const methods: { [key: string]: (...any: any) => any } = { */ export function load() { console.log('[Cocos ECS Extension] 扩展已加载'); + + // 初始化热更新系统 + HotUpdateHandler.initialize().catch(error => { + console.error('[Cocos ECS Extension] 热更新初始化失败:', error); + }); } /** @@ -195,4 +223,7 @@ export function load() { */ export function unload() { console.log('[Cocos ECS Extension] 扩展已卸载'); + + // 清理热更新资源 + HotUpdateHandler.cleanup(); } diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/start-admin.bat b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/start-admin.bat new file mode 100644 index 00000000..cc206ee1 --- /dev/null +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/start-admin.bat @@ -0,0 +1,50 @@ +@echo off +chcp 65001 >nul +title Cocos ECS Extension - 热更新管理后台 + +echo. +echo ====================================== +echo 🚀 Cocos ECS Extension 热更新管理后台 +echo ====================================== +echo. + +:: 检查Node.js是否安装 +node --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo ❌ 错误: 未检测到Node.js,请先安装Node.js + echo 下载地址: https://nodejs.org/ + pause + exit /b 1 +) + +:: 获取Node.js版本 +for /f "tokens=1" %%i in ('node --version') do set NODE_VERSION=%%i +echo ✅ Node.js版本: %NODE_VERSION% + +:: 检查是否首次运行 +if not exist "admin-backend\node_modules" ( + echo. + echo 📦 首次运行,正在安装依赖... + cd admin-backend + call npm install + if %errorlevel% neq 0 ( + echo ❌ 依赖安装失败 + pause + exit /b 1 + ) + cd .. + echo ✅ 依赖安装完成 +) + +:: 启动服务 +echo. +echo 🚀 启动热更新管理后台... +echo 📍 管理界面地址: http://localhost:3001 +echo. +echo 💡 提示: 按 Ctrl+C 可停止服务 +echo. + +cd admin-backend +call npm run dev + +pause \ No newline at end of file diff --git a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/tsconfig.json b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/tsconfig.json index 78832ecd..f6bfc7cf 100644 --- a/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/tsconfig.json +++ b/extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension/tsconfig.json @@ -7,5 +7,9 @@ "node", "@cocos/creator-types/editor", ] - } + }, + "exclude": [ + "admin-backend/**/*", + "admin-desktop/**/*" + ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c586d976..79fcf2df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "2.1.22", "license": "MIT", "dependencies": { + "@types/multer": "^1.4.13", "@types/ws": "^8.18.1", "ws": "^8.18.2" }, @@ -574,12 +575,68 @@ "win32" ] }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, + "node_modules/@types/express": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "node_modules/@types/multer": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", + "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "20.19.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", @@ -589,12 +646,41 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "dev": true }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", diff --git a/package.json b/package.json index 563b00c9..b40c520a 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "url": "https://github.com/esengine/ecs-framework.git" }, "dependencies": { + "@types/multer": "^1.4.13", "@types/ws": "^8.18.1", "ws": "^8.18.2" }