From 808f5f348eb7084d91827c8d9fda5c03835155ca Mon Sep 17 00:00:00 2001 From: BurntToasters <61037367+BurntToasters@users.noreply.github.com> Date: Thu, 11 Jun 2026 11:40:53 -0700 Subject: [PATCH 1/3] fixes and pkg updates --- com.burnttoasters.rosi.metainfo.xml | 2 +- package-lock.json | 1192 ++-------------------- package.json | 2 +- src/main/constants.ts | 11 + src/main/deno.ts | 11 +- src/main/download/commandBuilders.ts | 13 +- src/main/downloader.ts | 65 +- src/main/main.ts | 166 ++- src/main/platform.ts | 18 +- src/main/preload.ts | 11 +- src/main/settings.ts | 43 +- src/main/updater.ts | 10 +- src/renderer/css/01-base.css | 11 +- src/renderer/css/02-sidebar.css | 10 + src/renderer/css/04-panels.css | 29 +- src/renderer/css/05-overlays.css | 30 +- src/renderer/css/06-responsive.css | 4 + src/renderer/css/07-queue.css | 12 + src/renderer/css/08-wizard.css | 10 +- src/renderer/index.html | 230 +++-- src/renderer/licenses-iframe.html | 6 +- src/renderer/modules/queue.ts | 64 +- src/renderer/modules/settings.ts | 1 + src/renderer/modules/ui.ts | 22 +- src/renderer/msStoreDetector.js | 4 + src/renderer/renderer-globals.d.ts | 3 +- src/renderer/rosiEngine.ts | 512 +++++++--- src/renderer/splash.html | 2 +- src/tests/commandBuilders.test.ts | 71 +- src/tests/deno.test.ts | 1 + src/tests/downloader.errorPaths.test.ts | 3 +- src/tests/ipcValidation.nullEdge.test.ts | 4 +- src/tests/ipcValidation.test.ts | 74 +- src/tests/main.ipc.test.ts | 74 +- src/tests/platform.env.test.ts | 20 +- src/tests/platform.resolveYtdlp.test.ts | 14 +- src/tests/preload.test.ts | 4 +- src/tests/queue.test.ts | 71 +- src/tests/renderer.contract.test.ts | 10 +- src/tests/rendererModules.test.ts | 137 +++ src/tests/rosiEngine.dom.test.ts | 2 +- src/tests/settings.extended.test.ts | 11 +- src/tests/updaterVersioning.test.ts | 8 +- src/tests/validation.test.ts | 30 +- src/tests/videoInfo.fetch.test.ts | 214 ++++ src/types.ts | 6 +- src/utils/ipcValidation.ts | 142 ++- src/utils/validation.ts | 64 +- vitest.config.ts | 2 +- 49 files changed, 1944 insertions(+), 1512 deletions(-) create mode 100644 src/tests/rendererModules.test.ts create mode 100644 src/tests/videoInfo.fetch.test.ts diff --git a/com.burnttoasters.rosi.metainfo.xml b/com.burnttoasters.rosi.metainfo.xml index 4541893..ffd4327 100644 --- a/com.burnttoasters.rosi.metainfo.xml +++ b/com.burnttoasters.rosi.metainfo.xml @@ -33,7 +33,7 @@ - + diff --git a/package-lock.json b/package-lock.json index a22b4c8..8d799e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rosi", - "version": "4.1.0-beta.1", + "version": "4.1.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rosi", - "version": "4.1.0-beta.1", + "version": "4.1.0-beta.2", "license": "MPL-2.0", "dependencies": { "electron-log": "^5.3.4", @@ -468,25 +468,18 @@ } }, "node_modules/@electron/rebuild": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.0.3.tgz", - "integrity": "sha512-u9vpTHRMkOYCs/1FLiSVAFZ7FbjsXK+bQuzviJZa+lG7BHZl1nz52/IcGvwa3sk80/fc3llutBkbCq10Vh8WQA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.0.4.tgz", + "integrity": "sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg==", "dev": true, "license": "MIT", "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.1.1", - "detect-libc": "^2.0.1", - "got": "^11.7.0", - "graceful-fs": "^4.2.11", "node-abi": "^4.2.0", "node-api-version": "^0.2.1", - "node-gyp": "^11.2.0", - "ora": "^5.1.0", - "read-binary-file-arch": "^1.0.6", - "semver": "^7.3.5", - "tar": "^7.5.6", - "yargs": "^17.0.1" + "node-gyp": "^12.2.0", + "read-binary-file-arch": "^1.0.6" }, "bin": { "electron-rebuild": "lib/cli.js" @@ -854,80 +847,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -1056,43 +975,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@npmcli/agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", - "integrity": "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@npmcli/fs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", - "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/@oxc-project/types": { "version": "0.133.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", @@ -1155,17 +1037,6 @@ "node": ">=14.18.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", @@ -2004,13 +1875,13 @@ } }, "node_modules/abbrev": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", - "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/acorn": { @@ -2109,9 +1980,9 @@ } }, "node_modules/app-builder-lib": { - "version": "26.15.0", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.15.0.tgz", - "integrity": "sha512-j2+P6Lh+l/VuWfXZWSs7u+OAPqYJQGnZZO30M833XQQaRuyohm4RZk7Gw4nQXfeyQH9GqXaTwR16Y0LaVTlS+g==", + "version": "26.15.1", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.15.1.tgz", + "integrity": "sha512-+YXdZcIbsHLIc/DKtPauZ0n4v5dNl89Rs9SHV062b/LZKl47dkXaE2J4LAgTo5sBuKWEbCkUMdJKXCkJVJpxpA==", "dev": true, "license": "MIT", "dependencies": { @@ -2120,7 +1991,7 @@ "@electron/get": "^3.0.0", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.3", - "@electron/rebuild": "4.0.3", + "@electron/rebuild": "^4.0.4", "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", "@noble/hashes": "^2.2.0", @@ -2137,7 +2008,7 @@ "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", - "electron-publish": "26.15.0", + "electron-publish": "26.15.1", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", @@ -2161,8 +2032,8 @@ "node": ">=14.0.0" }, "peerDependencies": { - "dmg-builder": "26.15.0", - "electron-builder-squirrel-windows": "26.15.0" + "dmg-builder": "26.15.1", + "electron-builder-squirrel-windows": "26.15.1" } }, "node_modules/app-builder-lib/node_modules/@electron/get": { @@ -2451,18 +2322,6 @@ "require-from-string": "^2.0.2" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -2492,31 +2351,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -2583,92 +2417,6 @@ "node": ">=6.0.0" } }, - "node_modules/cacache": { - "version": "19.0.1", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz", - "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^4.0.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^7.0.2", - "ssri": "^12.0.0", - "tar": "^7.4.3", - "unique-filename": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/cacache/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -2788,19 +2536,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-truncate": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", @@ -2899,16 +2634,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -3126,19 +2851,6 @@ "dev": true, "license": "MIT" }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/defer-to-connect": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", @@ -3258,13 +2970,13 @@ } }, "node_modules/dmg-builder": { - "version": "26.15.0", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.15.0.tgz", - "integrity": "sha512-oS8MWttbpIUF/2v8LOEY+f4ayL84ipMOarZvdRMl/pxlhLxAYjYMklTXHEXIl37Ig+qJv/bVF7HgyIoOoZyMWA==", + "version": "26.15.1", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.15.1.tgz", + "integrity": "sha512-9qyIXJpR9dBnkhsClbht/addUeJJYIj95Ofx0wlKoyK1d3RzlCdUduwGElLWONOlGZio287CHtblgrxvu+uCbw==", "dev": true, "license": "MIT", "dependencies": { - "app-builder-lib": "26.15.0", + "app-builder-lib": "26.15.1", "builder-util": "26.15.0", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0" @@ -3353,46 +3065,6 @@ "readable-stream": "^2.0.2" } }, - "node_modules/duplexer2/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==", - "dev": true, - "license": "MIT", - "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/duplexer2/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==", - "dev": true, - "license": "MIT" - }, - "node_modules/duplexer2/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -3429,18 +3101,18 @@ } }, "node_modules/electron-builder": { - "version": "26.15.0", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.15.0.tgz", - "integrity": "sha512-zd4cfvjHmtyGqMaDudg5rAjNUkwIJDz8ICaCsz77hFKcjMQHcZNNNCs/C4phwN9+gEVwmhvpKMzNFum6fs/n6A==", + "version": "26.15.1", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.15.1.tgz", + "integrity": "sha512-qqhv7Da4M5pwHZA2dF94PVwIJiQXiXPhAX7Ogo6KAdzMbBoNsM0g8vSMcWtUPDfpC+Z1coq+p9fXn9U4uDeo1w==", "dev": true, "license": "MIT", "dependencies": { - "app-builder-lib": "26.15.0", + "app-builder-lib": "26.15.1", "builder-util": "26.15.0", "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "ci-info": "^4.2.0", - "dmg-builder": "26.15.0", + "dmg-builder": "26.15.1", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", @@ -3455,14 +3127,14 @@ } }, "node_modules/electron-builder-squirrel-windows": { - "version": "26.15.0", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.15.0.tgz", - "integrity": "sha512-WyiGTdAHOk8oncyg/Ik0+NsrPI6y7ftG/eB7uxr9iGjmF9RNuIdeW/BRpI7iLEe718WkBQCbspUAq/4Vi31wXg==", + "version": "26.15.1", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.15.1.tgz", + "integrity": "sha512-kPEqUP4Bnpb3oRhfN2/19FbRDYwFoq+tXEM87m3clK/AotegS3pKx0tSTrWtM7pEZ0zw5V/tuoo2hRwig1ax8Q==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "app-builder-lib": "26.15.0", + "app-builder-lib": "26.15.1", "builder-util": "26.15.0", "electron-winstaller": "5.4.0" } @@ -3477,9 +3149,9 @@ } }, "node_modules/electron-publish": { - "version": "26.15.0", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.15.0.tgz", - "integrity": "sha512-pt6K3ol/a+o3HbqmYkL2NYlVH5pd34tL4FPRcgX8E88xQAqQyIsseXe4vWy7Pq2BaYy+iFGJrtInZe11FFAQwQ==", + "version": "26.15.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.15.1.tgz", + "integrity": "sha512-BMgMHOyexWn0UnOC+Afffw0DMrr0yfLp4U8YsLXwoJ3Da7LS7WUnz21teYZqO0gaApE1KgsjREWmbPqvF5JcPg==", "dev": true, "license": "MIT", "dependencies": { @@ -3606,17 +3278,6 @@ "dev": true, "license": "MIT" }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -4148,36 +3809,6 @@ "dev": true, "license": "ISC" }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -4209,19 +3840,6 @@ "node": ">=12" } }, - "node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4644,41 +4262,6 @@ "url": "https://github.com/sponsors/typicode" } }, - "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==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "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==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4718,16 +4301,6 @@ "dev": true, "license": "ISC" }, - "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4767,16 +4340,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -4784,19 +4347,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -4863,22 +4413,6 @@ "node": ">=8" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jake": { "version": "10.9.4", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", @@ -5422,23 +4956,6 @@ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", "license": "MIT" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/log-update": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", @@ -5593,29 +5110,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-fetch-happen": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz", - "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^3.0.0", - "cacache": "^19.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^5.0.0", - "promise-retry": "^2.0.1", - "ssri": "^12.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", @@ -5683,16 +5177,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", @@ -5752,115 +5236,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minipass-fetch": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz", - "integrity": "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^3.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", - "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-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==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/minizlib": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", @@ -5920,16 +5295,6 @@ "dev": true, "license": "MIT" }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/node-abi": { "version": "4.31.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.31.0.tgz", @@ -5954,28 +5319,28 @@ } }, "node_modules/node-gyp": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz", - "integrity": "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^14.0.3", - "nopt": "^8.0.0", - "proc-log": "^5.0.0", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^7.4.3", + "tar": "^7.5.4", "tinyglobby": "^0.2.12", - "which": "^5.0.0" + "undici": "^6.25.0", + "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/node-gyp/node_modules/env-paths": { @@ -5989,29 +5354,39 @@ } }, "node_modules/node-gyp/node_modules/isexe": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", - "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { - "node": ">=18" + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/undici": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.26.0.tgz", + "integrity": "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" } }, "node_modules/node-gyp/node_modules/which": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "dev": true, "license": "ISC", "dependencies": { - "isexe": "^3.1.1" + "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/node-int64": { @@ -6022,19 +5397,19 @@ "license": "MIT" }, "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", "dev": true, "license": "ISC", "dependencies": { - "abbrev": "^3.0.0" + "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/normalize-url": { @@ -6119,96 +5494,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -6251,26 +5536,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", - "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/parse5": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", @@ -6314,30 +5579,6 @@ "node": ">=8" } }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -6519,13 +5760,13 @@ } }, "node_modules/proc-log": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", - "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/process-nextick-args": { @@ -6639,18 +5880,19 @@ } }, "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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "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/require-directory": { @@ -6827,34 +6069,12 @@ } }, "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==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT" }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/sanitize-filename": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", @@ -7004,47 +6224,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", - "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ip-address": "^10.1.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7084,19 +6263,6 @@ "license": "BSD-3-Clause", "optional": true }, - "node_modules/ssri": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", - "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -7122,13 +6288,13 @@ "license": "MIT" }, "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "safe-buffer": "~5.1.0" } }, "node_modules/string-argv": { @@ -7158,55 +6324,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-width-cjs": { - "name": "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", @@ -7223,30 +6340,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -7564,9 +6657,9 @@ } }, "node_modules/undici": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.1.tgz", - "integrity": "sha512-UDdpiex+mzigiyrXrGbiUaF4HzTNhKbh2vRNFaTMzcqmLIPrZxaCtwo/1TMSuWoM1Xz3WiTo9KdgI3kRqYzJGg==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.2.tgz", + "integrity": "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==", "dev": true, "license": "MIT", "engines": { @@ -7580,32 +6673,6 @@ "dev": true, "license": "MIT" }, - "node_modules/unique-filename": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", - "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/unique-slug": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz", - "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -7848,16 +6915,6 @@ "node": ">=18" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/webcrypto-core": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.9.2.tgz", @@ -7968,73 +7025,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", diff --git a/package.json b/package.json index 5425aed..62f1df1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rosi", - "version": "4.1.0-beta.1", + "version": "4.1.0-beta.2", "private": true, "description": "Electron GUI for yt-dlp", "keywords": [ diff --git a/src/main/constants.ts b/src/main/constants.ts index 3ca9da1..d343515 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -13,6 +13,17 @@ export const SUBTITLE_LANGS_PATTERN = /^[A-Za-z0-9.*-]+(,[A-Za-z0-9.*-]+)*$/; export const ALLOWED_AUDIO_FORMATS = new Set(['mp3', 'flac', 'ogg', 'wav', 'm4a', 'opus']); export const ALLOWED_CONVERT_FORMATS = new Set(['mp4', 'mov', 'mp3', 'm4a']); +export const ALLOWED_BROWSERS = new Set([ + 'brave', + 'chrome', + 'chromium', + 'edge', + 'firefox', + 'opera', + 'safari', + 'vivaldi', + 'whale', +]); export const MAX_QUEUE_SIZE = 500; export const MAX_FORMAT_COUNTS = 10_000; export const MAX_SETTINGS_IMPORT_BYTES = 1_048_576; diff --git a/src/main/deno.ts b/src/main/deno.ts index eb664be..894d983 100644 --- a/src/main/deno.ts +++ b/src/main/deno.ts @@ -1,6 +1,5 @@ import * as path from 'path'; import * as fs from 'fs'; -import { spawn } from 'child_process'; import { BrowserWindow, dialog } from 'electron'; import log from 'electron-log/main.js'; import type { MessageBoxOptions } from 'electron'; @@ -10,7 +9,7 @@ import { MAX_OUTPUT_BUFFER, MAX_ERROR_BUFFER, } from './constants'; -import { buildEnhancedPath, isWindows } from './platform'; +import { spawnWithEnv, isWindows } from './platform'; function getDenoSearchPaths(): string[] { if (isWindows) { @@ -45,9 +44,7 @@ export async function checkDenoInstalled(): Promise { } const checkCmd = isWindows ? 'where' : 'which'; - const proc = spawn(checkCmd, ['deno'], { - env: { PATH: buildEnhancedPath() }, - }); + const proc = spawnWithEnv(checkCmd, ['deno']); const timeout = setTimeout(() => { try { @@ -107,9 +104,7 @@ export async function installDeno( installArgs = ['-c', 'curl -fsSL https://deno.land/install.sh | sh']; } - const proc = spawn(installCmd, installArgs, { - env: { ...process.env, PATH: buildEnhancedPath() }, - }); + const proc = spawnWithEnv(installCmd, installArgs); let output = ''; let error = ''; let settled = false; diff --git a/src/main/download/commandBuilders.ts b/src/main/download/commandBuilders.ts index 8e4f9db..e32ca8c 100644 --- a/src/main/download/commandBuilders.ts +++ b/src/main/download/commandBuilders.ts @@ -4,6 +4,7 @@ import { detectGpu } from '../gpu'; import { spawnWithEnv } from '../platform'; import { ALLOWED_AUDIO_FORMATS, + ALLOWED_BROWSERS, FORMAT_ID_PATTERN, MAX_ERROR_BUFFER, SUBTITLE_LANGS_PATTERN, @@ -11,17 +12,7 @@ import { const VALID_FORMAT_ID = FORMAT_ID_PATTERN; const CODEC_PROBE_TIMEOUT_MS = 30_000; -const ALLOWED_BROWSERS = new Set([ - 'brave', - 'chrome', - 'chromium', - 'edge', - 'firefox', - 'opera', - 'safari', - 'vivaldi', - 'whale', -]); +export { ALLOWED_BROWSERS }; export interface SourceCodecs { video?: string; diff --git a/src/main/downloader.ts b/src/main/downloader.ts index 1772aba..22c23e6 100644 --- a/src/main/downloader.ts +++ b/src/main/downloader.ts @@ -1,5 +1,6 @@ import * as path from 'path'; import * as fs from 'fs'; +import { exec } from 'child_process'; import sanitize from 'sanitize-filename'; import { dialog } from 'electron'; import log from 'electron-log/main.js'; @@ -29,6 +30,7 @@ import { import type { ChildProcess } from 'child_process'; import type { DownloadSession, + DownloadSessionOwner, DownloadRequestOptions, DownloadOutcome, FormatsProcess, @@ -36,9 +38,23 @@ import type { } from '../types'; let activeDownloadSession: DownloadSession | null = null; +let downloadSessionOwner: DownloadSessionOwner | null = null; let downloadSessionCounter = 0; let formatsProcess: FormatsProcess | null = null; +export function getDownloadSessionOwner(): DownloadSessionOwner | null { + return downloadSessionOwner; +} + +export function isDownloadBusy(): boolean { + return activeDownloadSession !== null; +} + +export function canStartDownload(owner: DownloadSessionOwner): boolean { + if (!activeDownloadSession) return true; + return downloadSessionOwner === owner; +} + function isActiveSession(session: DownloadSession | null) { return Boolean(session && activeDownloadSession && activeDownloadSession.id === session.id); } @@ -102,6 +118,7 @@ function completeSession( } activeDownloadSession = null; + downloadSessionOwner = null; } function killProcess(proc: ChildProcess | null, label: string) { @@ -110,7 +127,12 @@ function killProcess(proc: ChildProcess | null, label: string) { proc.kill('SIGTERM'); const forceKillTimer = setTimeout(() => { try { - if (!proc.killed) proc.kill('SIGKILL'); + if (!proc.killed) { + proc.kill('SIGKILL'); + if (isWindows && proc.pid) { + exec(`taskkill /PID ${proc.pid} /T /F`, () => {}); + } + } } catch {} }, 5000); proc.once('exit', () => clearTimeout(forceKillTimer)); @@ -130,7 +152,7 @@ export function cancelActiveSession(notify = true) { if (!isActiveSession(session)) return; - if (notify) { + if (notify || session.owner === 'manual') { completeSession(session, '⏹️ Cancelled.', 'cancelled', { progressMessage: '⏹️ Download/Conversion cancelled by user.', }); @@ -148,16 +170,30 @@ export function cancelActiveSession(notify = true) { recordDownload('cancelled'); session.lifecycle = markTerminalEventEmitted(session.lifecycle); activeDownloadSession = null; + downloadSessionOwner = null; } export function killAllProcesses() { - if (activeDownloadSession) { - killProcess(activeDownloadSession.ytdlpProcess, 'yt-dlp'); - killProcess(activeDownloadSession.ffmpegProcess, 'ffmpeg'); - activeDownloadSession.ytdlpProcess = null; - activeDownloadSession.ffmpegProcess = null; - activeDownloadSession = null; + if (!activeDownloadSession) return; + const session = activeDownloadSession; + session.lifecycle = markDownloadCancelled(session.lifecycle); + killProcess(session.ytdlpProcess, 'yt-dlp'); + killProcess(session.ffmpegProcess, 'ffmpeg'); + session.ytdlpProcess = null; + session.ffmpegProcess = null; + + if (typeof session.onComplete === 'function') { + try { + session.onComplete('⏹️ Cancelled.', 'cancelled'); + } catch (error) { + log.error('Error in download cancellation callback:', error); + } } + + recordDownload('cancelled'); + session.lifecycle = markTerminalEventEmitted(session.lifecycle); + activeDownloadSession = null; + downloadSessionOwner = null; } export function fetchFormats(ytdlpPath: string, url: string): Promise { @@ -176,7 +212,7 @@ export function fetchFormats(ytdlpPath: string, url: string): Promise { log.warn('Error killing previous formats process:', error); } } - const proc = spawnWithEnv(ytdlpPath, ['-F', url]); + const proc = spawnWithEnv(ytdlpPath, ['-F', '--', url]); formatsProcess = { proc, cancelled: false }; let outputData = ''; let errorData = ''; @@ -317,6 +353,7 @@ async function runConversion( if (!isActiveSession(session) || !ffProc || ffProc.killed) return; sendProgress(session, '❌ Conversion timed out after 10 minutes.'); killProcess(ffProc, 'ffmpeg-timeout'); + completeSession(session, '❌ Conversion failed (timeout).', 'failed'); }, FFMPEG_CONVERT_TIMEOUT_MS); ffProc.stdout?.on('data', (data) => { @@ -424,8 +461,12 @@ export function startDownload( sender: Electron.WebContents, options: DownloadRequestOptions, mainWindow: Electron.BrowserWindow | null, - onComplete?: (statusMessage: string) => void + onComplete?: (statusMessage: string, outcome?: DownloadOutcome) => void, + owner: DownloadSessionOwner = 'manual' ) { + if (activeDownloadSession && downloadSessionOwner !== owner) { + throw new Error('Download session already active with a different owner.'); + } if (activeDownloadSession) { cancelActiveSession(false); } @@ -434,12 +475,14 @@ export function startDownload( const session: DownloadSession = { id: downloadSessionCounter, sender, + owner, lifecycle: createDownloadLifecycleState(), ytdlpProcess: null, ffmpegProcess: null, onComplete, }; activeDownloadSession = session; + downloadSessionOwner = owner; const settings = loadSettings(); const effectiveSettings: Settings = { ...settings }; @@ -514,7 +557,7 @@ export function startDownload( sendProgress(session, `🚀 Starting download: ${url}`); sendProgress(session, ` Command: ${ytdlpBinary} ${ytdlpArgs.join(' ')}`); const ytProc = spawnWithEnv(ytdlpPath, ytdlpArgs, { - env: { ...process.env, PYTHONUNBUFFERED: '1' }, + env: { PYTHONUNBUFFERED: '1' }, }); session.ytdlpProcess = ytProc; diff --git a/src/main/main.ts b/src/main/main.ts index 4b4a776..099ac5e 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,6 +1,7 @@ import { app, BrowserWindow, ipcMain, dialog, shell, Notification } from 'electron'; import * as path from 'path'; import * as fs from 'fs'; +import { randomUUID } from 'crypto'; import log from 'electron-log/main.js'; import { isPackaged, resolveYtdlpPath, verifyBundledFfmpeg } from './platform'; import { @@ -27,6 +28,7 @@ import { killAllProcesses, fetchFormats, cancelFormats, + canStartDownload, } from './downloader'; import { fetchVideoInfo, cancelVideoInfo } from './download/videoInfo'; import { isSafeExternalUrl, isSafeHttpUrl, isAllowedNavigationUrl } from '../utils/validation'; @@ -44,7 +46,7 @@ import type { DownloadRequestOptions, DownloadOutcome, QueueItem } from '../type log.initialize(); -process.setMaxListeners(20); +process.setMaxListeners(32); process.on('uncaughtException', (error) => { log.error('Uncaught exception:', error); @@ -54,6 +56,9 @@ process.on('uncaughtException', (error) => { try { cancelFormats(); } catch {} + try { + cancelVideoInfo(); + } catch {} try { dialog.showErrorBox( 'Fatal Error', @@ -69,7 +74,13 @@ process.on('unhandledRejection', (reason) => { log.error('Unhandled rejection:', reason); }); -const ytdlpPath = resolveYtdlpPath(); +let ytdlpPath: string | null = null; +function getYtdlpPath(): string { + if (!ytdlpPath) { + ytdlpPath = resolveYtdlpPath(); + } + return ytdlpPath; +} const isSmokeRun = process.argv.includes('--smoke') || process.env.ROSI_SMOKE === '1'; const isPrimaryInstance = isSmokeRun || typeof app.requestSingleInstanceLock !== 'function' @@ -82,11 +93,20 @@ let splashWindow: BrowserWindow | null = null; let mainWindowCloseInProgress = false; let mainWindowCloseTimer: NodeJS.Timeout | null = null; let appQuitting = false; +let isInstallingUpdate = false; +let renderProcessReloadCount = 0; function getMainWindow() { return mainWindow; } +function assertMainWindowSender(event?: { sender?: { id?: number } }): boolean { + if (!mainWindow || mainWindow.isDestroyed()) { + return true; + } + return event?.sender?.id === mainWindow.webContents.id; +} + function clearMainWindowCloseTimer() { if (mainWindowCloseTimer) { clearTimeout(mainWindowCloseTimer); @@ -106,11 +126,18 @@ function createSplashWindow() { contextIsolation: true, nodeIntegration: false, sandbox: true, + webSecurity: true, }, roundedCorners: true, }); void splashWindow.loadFile(path.join(__dirname, '..', '..', 'src', 'renderer', 'splash.html')); splashWindow.center(); + setTimeout(() => { + if (splashWindow && !splashWindow.isDestroyed()) { + splashWindow.close(); + splashWindow = null; + } + }, 30_000); } async function runRendererSmokeChecks(windowRef: BrowserWindow): Promise { @@ -266,7 +293,7 @@ function createWindow() { clearMainWindowCloseTimer(); mainWindow.on('close', (event) => { - if (isSmokeRun || mainWindowCloseInProgress) { + if (isSmokeRun || mainWindowCloseInProgress || isInstallingUpdate) { return; } if (!mainWindow || mainWindow.isDestroyed()) { @@ -295,6 +322,7 @@ function createWindow() { mainWindow.on('closed', () => { clearMainWindowCloseTimer(); mainWindowCloseInProgress = false; + isInstallingUpdate = false; mainWindow = null; }); @@ -310,12 +338,20 @@ function createWindow() { mainWindow.webContents.on('render-process-gone', (_event, details) => { log.error(`Renderer process gone: ${details.reason} (exit code: ${details.exitCode})`); if (details.reason !== 'clean-exit' && mainWindow && !mainWindow.isDestroyed()) { - mainWindow.reload(); + if (renderProcessReloadCount < 3) { + renderProcessReloadCount += 1; + mainWindow.reload(); + return; + } + dialog.showErrorBox( + 'Renderer Error', + 'The application window failed to recover after multiple reload attempts. Please restart ROSI.' + ); } }); mainWindow.webContents.on('will-navigate', (event, url) => { - if (!isAllowedNavigationUrl(url)) { + if (!isAllowedNavigationUrl(url, app.getAppPath())) { event.preventDefault(); if (isSafeExternalUrl(url)) { shell.openExternal(url).catch((err) => { @@ -384,6 +420,16 @@ void app.whenReady().then(() => { return; } + const resolvedYtdlpPath = getYtdlpPath(); + if (!fs.existsSync(resolvedYtdlpPath)) { + dialog.showErrorBox( + 'Missing Dependency', + `yt-dlp binary not found at ${resolvedYtdlpPath}.\nPlease ensure the yt-dlp binary is in the application's directory.` + ); + app.quit(); + return; + } + if (!isSmokeRun) { createSplashWindow(); } @@ -456,7 +502,11 @@ if (!process.windowsStore) { ipcMain.handle('check-for-updates', () => checkForUpdates(isPackaged, loadSettings)); ipcMain.handle('download-update', () => downloadUpdate()); ipcMain.on('cancel-update-download', () => cancelUpdateDownload(getMainWindow)); - ipcMain.on('install-update', () => installUpdate()); + ipcMain.on('install-update', () => { + isInstallingUpdate = true; + clearMainWindowCloseTimer(); + installUpdate(); + }); } ipcMain.handle('check-deno-installed', () => checkDenoInstalled()); @@ -479,7 +529,7 @@ ipcMain.handle('save-settings', (_, data) => { ipcMain.handle('detect-gpu', () => detectGpu()); ipcMain.on('reset-settings', (event) => { - if (mainWindow && !mainWindow.isDestroyed() && event.sender?.id !== mainWindow.webContents.id) { + if (!assertMainWindowSender(event)) { return; } try { @@ -536,7 +586,7 @@ ipcMain.handle('getFormats', async (_, url) => { } try { - const formats = await fetchFormats(ytdlpPath, url); + const formats = await fetchFormats(getYtdlpPath(), url); return okResult(formats); } catch (error) { const message = @@ -552,7 +602,7 @@ ipcMain.handle('getFormats', async (_, url) => { } }); ipcMain.on('cancel-formats', (event) => { - if (mainWindow && !mainWindow.isDestroyed() && event.sender?.id !== mainWindow.webContents.id) { + if (!assertMainWindowSender(event)) { return; } cancelFormats(); @@ -564,7 +614,7 @@ ipcMain.handle('get-video-info', async (_, url) => { } try { - const info = await fetchVideoInfo(ytdlpPath, url); + const info = await fetchVideoInfo(getYtdlpPath(), url); return okResult(info); } catch (error) { const message = @@ -581,23 +631,33 @@ ipcMain.handle('get-video-info', async (_, url) => { }); ipcMain.on('cancel-video-info', (event) => { - if (mainWindow && !mainWindow.isDestroyed() && event.sender?.id !== mainWindow.webContents.id) { + if (!assertMainWindowSender(event)) { return; } cancelVideoInfo(); }); ipcMain.handle('download-video', (event, options) => { - if (mainWindow && !mainWindow.isDestroyed() && event.sender?.id !== mainWindow.webContents.id) { + if (!assertMainWindowSender(event)) { return errorResult('VALIDATION_ERROR', 'Unauthorized sender.'); } const validation = validateDownloadRequestPayload(options); if (!validation.ok) { return errorResult(validation.error.code, validation.error.message, validation.error.details); } + if (!canStartDownload('manual')) { + return errorResult('NOT_AVAILABLE', 'A queue download is already in progress.'); + } try { - startDownload(ytdlpPath, event.sender, validation.data as DownloadRequestOptions, mainWindow); + startDownload( + getYtdlpPath(), + event.sender, + validation.data as DownloadRequestOptions, + mainWindow, + undefined, + 'manual' + ); return okResult({ started: true }); } catch (error) { log.error('Error in download-video handler:', error); @@ -606,7 +666,7 @@ ipcMain.handle('download-video', (event, options) => { }); ipcMain.on('cancel-download', (event) => { - if (mainWindow && !mainWindow.isDestroyed() && event.sender?.id !== mainWindow.webContents.id) { + if (!assertMainWindowSender(event)) { return; } try { @@ -617,6 +677,15 @@ ipcMain.on('cancel-download', (event) => { }); ipcMain.handle('restart-app', () => { + appQuitting = true; + try { + cancelActiveSession(false); + killAllProcesses(); + cancelFormats(); + cancelVideoInfo(); + } catch (error) { + log.error('Error cleaning up before restart:', error); + } app.relaunch(); app.exit(0); }); @@ -667,7 +736,7 @@ ipcMain.handle('show-notification', (_, options) => { silent: false, }); - notification.on('click', () => { + notification.once('click', () => { try { const win = getMainWindow(); if (win && !win.isDestroyed()) { @@ -705,8 +774,11 @@ ipcMain.handle('export-settings', async () => { ipcMain.handle('import-settings', async () => { try { const parentWindow = mainWindow && !mainWindow.isDestroyed() ? mainWindow : null; - const success = await importSettingsFromFile(parentWindow); - if (!success) return errorResult('INTERNAL_ERROR', 'Import cancelled or failed.'); + const importedSettings = await importSettingsFromFile(parentWindow); + if (!importedSettings) return errorResult('INTERNAL_ERROR', 'Import cancelled or failed.'); + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('settings-imported', importedSettings); + } return okResult({ imported: true }); } catch (error) { log.error('Error importing settings:', error); @@ -767,6 +839,8 @@ function loadPersistedQueue(): QueueItem[] { return []; } +let persistQueueTimer: NodeJS.Timeout | null = null; + function persistQueue(): void { const tempPath = `${queuePath}.tmp`; try { @@ -791,14 +865,24 @@ function persistQueue(): void { downloadQueue = loadPersistedQueue(); function generateQueueId(): string { - return `q_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; + return `q_${randomUUID()}`; +} + +function schedulePersistQueue(): void { + if (persistQueueTimer) { + clearTimeout(persistQueueTimer); + } + persistQueueTimer = setTimeout(() => { + persistQueueTimer = null; + persistQueue(); + }, 300); } function broadcastQueue() { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.webContents.send('queue-update', downloadQueue); } - persistQueue(); + schedulePersistQueue(); } async function processQueue() { @@ -830,9 +914,13 @@ async function processQueue() { } const settings = loadSettings(); + const downloadFolder = + settings.downloadFolder && settings.downloadFolder.trim() !== '' + ? settings.downloadFolder + : app.getPath('downloads'); const options: DownloadRequestOptions = { url: nextItem.url, - outputPath: app.getPath('downloads'), + outputPath: downloadFolder, ffmpegPath: settings.ffmpegPath || undefined, convertFormat: settings.convertEnabled ? settings.convertFormat : undefined, keepOriginal: settings.convertEnabled ? settings.keepOriginalAfterConvert : undefined, @@ -860,7 +948,14 @@ async function processQueue() { }; try { - startDownload(ytdlpPath, mainWindow.webContents, options, mainWindow, completeListener); + startDownload( + getYtdlpPath(), + mainWindow.webContents, + options, + mainWindow, + completeListener, + 'queue' + ); } catch (error) { nextItem.status = 'failed'; nextItem.error = (error as Error).message; @@ -878,7 +973,7 @@ async function processQueue() { } ipcMain.handle('add-to-queue', (event, urls) => { - if (mainWindow && !mainWindow.isDestroyed() && event.sender?.id !== mainWindow.webContents.id) { + if (!assertMainWindowSender(event)) { return errorResult('VALIDATION_ERROR', 'Unauthorized sender.'); } if (!Array.isArray(urls)) { @@ -903,7 +998,7 @@ ipcMain.handle('add-to-queue', (event, urls) => { }); ipcMain.handle('remove-from-queue', (event, id) => { - if (mainWindow && !mainWindow.isDestroyed() && event.sender?.id !== mainWindow.webContents.id) { + if (!assertMainWindowSender(event)) { return errorResult('VALIDATION_ERROR', 'Unauthorized sender.'); } if (typeof id !== 'string') { @@ -921,7 +1016,10 @@ ipcMain.handle('remove-from-queue', (event, id) => { return okResult(undefined); }); -ipcMain.handle('clear-queue', () => { +ipcMain.handle('clear-queue', (event) => { + if (!assertMainWindowSender(event)) { + return errorResult('VALIDATION_ERROR', 'Unauthorized sender.'); + } if (isQueueRunning) { queueCancelled = true; cancelActiveSession(true); @@ -933,9 +1031,17 @@ ipcMain.handle('clear-queue', () => { return okResult(undefined); }); -ipcMain.handle('get-queue', () => downloadQueue); +ipcMain.handle('get-queue', (event) => { + if (!assertMainWindowSender(event)) { + return errorResult('VALIDATION_ERROR', 'Unauthorized sender.'); + } + return downloadQueue; +}); -ipcMain.handle('start-queue', () => { +ipcMain.handle('start-queue', (event) => { + if (!assertMainWindowSender(event)) { + return errorResult('VALIDATION_ERROR', 'Unauthorized sender.'); + } const pending = downloadQueue.filter((item) => item.status === 'pending'); if (pending.length === 0) { return errorResult('NOT_AVAILABLE', 'No pending items in queue.'); @@ -943,13 +1049,19 @@ ipcMain.handle('start-queue', () => { if (isQueueRunning) { return errorResult('VALIDATION_ERROR', 'Queue is already running.'); } + if (!canStartDownload('queue')) { + return errorResult('NOT_AVAILABLE', 'A manual download is already in progress.'); + } isQueueRunning = true; queueCancelled = false; void processQueue(); return okResult({ started: true }); }); -ipcMain.handle('cancel-queue', () => { +ipcMain.handle('cancel-queue', (event) => { + if (!assertMainWindowSender(event)) { + return errorResult('VALIDATION_ERROR', 'Unauthorized sender.'); + } try { queueCancelled = true; isQueueRunning = false; diff --git a/src/main/platform.ts b/src/main/platform.ts index d584bd9..1c9e8d5 100644 --- a/src/main/platform.ts +++ b/src/main/platform.ts @@ -339,29 +339,21 @@ export function resolveYtdlpPath(): string { fs.chmodSync(tmpBin, 0o755); resolved = tmpBin; } catch (copyErr) { - dialog.showErrorBox( - 'Permission Error', - `Failed to prepare yt-dlp for execution at ${resolved}.\nError: ${(copyErr as Error).message}` + log.error( + `Failed to prepare yt-dlp for execution at ${resolved}: ${(copyErr as Error).message}` ); - app.quit(); } } } else { - dialog.showErrorBox( - 'Permission Error', - `Failed to set executable permissions on yt-dlp binary at ${resolved}.\nError: ${(err as Error).message}` + log.error( + `Failed to set executable permissions on yt-dlp binary at ${resolved}: ${(err as Error).message}` ); - app.quit(); } } } if (!fs.existsSync(resolved)) { - dialog.showErrorBox( - 'Missing Dependency', - `yt-dlp binary not found at ${resolved}.\nPlease ensure ${ytdlpBinary} is in the application's directory.` - ); - app.quit(); + log.error(`yt-dlp binary not found at ${resolved}`); } return resolved; diff --git a/src/main/preload.ts b/src/main/preload.ts index 38e98e8..ae797d8 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -45,12 +45,6 @@ const api: RendererApi = { ipcRenderer.on('updater-progress', listener); return () => ipcRenderer.removeListener('updater-progress', listener); }, - onDownloadProgress: (callback: (data: Record) => void) => { - const listener = (_: Electron.IpcRendererEvent, data: Record) => - callback(data); - ipcRenderer.on('download-progress', listener); - return () => ipcRenderer.removeListener('download-progress', listener); - }, onProgress: (callback: (message: string) => void) => { const listener = (_: Electron.IpcRendererEvent, message: string) => callback(message); ipcRenderer.on('progress', listener); @@ -88,6 +82,11 @@ const api: RendererApi = { ipcRenderer.on('queue-update', listener); return () => ipcRenderer.removeListener('queue-update', listener); }, + onSettingsImported: (callback: (settings: Settings) => void) => { + const listener = (_: Electron.IpcRendererEvent, data: Settings) => callback(data); + ipcRenderer.on('settings-imported', listener); + return () => ipcRenderer.removeListener('settings-imported', listener); + }, }; contextBridge.exposeInMainWorld('api', api); diff --git a/src/main/settings.ts b/src/main/settings.ts index 9bcd7a2..06f89a4 100644 --- a/src/main/settings.ts +++ b/src/main/settings.ts @@ -5,12 +5,14 @@ import log from 'electron-log/main.js'; import type { AudioFormat, DownloadStats, Settings } from '../types'; import { ALLOWED_AUDIO_FORMATS, + ALLOWED_BROWSERS, ALLOWED_CONVERT_FORMATS, MAX_FORMAT_COUNTS, MAX_SETTINGS_IMPORT_BYTES, CURRENT_SETTINGS_VERSION, SUBTITLE_LANGS_PATTERN, } from './constants'; +import { clearGpuCache } from './gpu'; const settingsPath = path.join(app.getPath('userData'), 'settings.json'); const statsPath = path.join(app.getPath('userData'), 'download-stats.json'); @@ -29,7 +31,7 @@ const defaultSettings: Settings = { keepOriginalAfterConvert: true, firstLaunch: true, hookBrowser: false, - browserChoice: 'Chrome', + browserChoice: 'chrome', animateBackground: true, notifications: true, denoReminderDismissed: false, @@ -37,6 +39,7 @@ const defaultSettings: Settings = { gpuType: 'auto', bestQuality: false, ffmpegPath: '', + downloadFolder: '', hideSupportModal: false, checkUpdatesOnStartup: true, updateChannel: 'auto', @@ -102,6 +105,26 @@ function readGpuType(value: unknown): Settings['gpuType'] { : defaultSettings.gpuType; } +function readBrowserChoice(value: unknown): string { + const raw = readString(value, defaultSettings.browserChoice); + const capped = raw.length > 64 ? raw.slice(0, 64) : raw; + const normalized = capped.trim().toLowerCase(); + if (ALLOWED_BROWSERS.has(normalized)) { + return normalized; + } + return defaultSettings.browserChoice; +} + +function readFfmpegPath(value: unknown): string { + const raw = readString(value, defaultSettings.ffmpegPath); + return raw.length > 1024 ? raw.slice(0, 1024) : raw; +} + +function readDownloadFolder(value: unknown): string { + const raw = readString(value, defaultSettings.downloadFolder); + return raw.length > 4096 ? raw.slice(0, 4096) : raw; +} + function readSettingsVersion(value: unknown): number { if ( typeof value === 'number' && @@ -138,7 +161,7 @@ export function migrateSettings(rawSettings: unknown): Settings { ), firstLaunch: readBoolean(rawSettings.firstLaunch, defaultSettings.firstLaunch), hookBrowser: readBoolean(rawSettings.hookBrowser, defaultSettings.hookBrowser), - browserChoice: readString(rawSettings.browserChoice, defaultSettings.browserChoice), + browserChoice: readBrowserChoice(rawSettings.browserChoice), animateBackground: readBoolean( rawSettings.animateBackground, defaultSettings.animateBackground @@ -151,7 +174,8 @@ export function migrateSettings(rawSettings: unknown): Settings { gpuAcceleration: readBoolean(rawSettings.gpuAcceleration, defaultSettings.gpuAcceleration), gpuType: readGpuType(rawSettings.gpuType), bestQuality: readBoolean(rawSettings.bestQuality, defaultSettings.bestQuality), - ffmpegPath: readString(rawSettings.ffmpegPath, defaultSettings.ffmpegPath), + ffmpegPath: readFfmpegPath(rawSettings.ffmpegPath), + downloadFolder: readDownloadFolder(rawSettings.downloadFolder), hideSupportModal: readBoolean(rawSettings.hideSupportModal, defaultSettings.hideSupportModal), checkUpdatesOnStartup: readBoolean( rawSettings.checkUpdatesOnStartup, @@ -201,6 +225,13 @@ export function saveSettings( const completeSettings = normalizeSettingsVersion( migrateSettings({ ...existing, ...newSettings }) ); + if ( + (newSettings.ffmpegPath !== undefined && newSettings.ffmpegPath !== existing.ffmpegPath) || + (newSettings.gpuAcceleration !== undefined && + newSettings.gpuAcceleration !== existing.gpuAcceleration) + ) { + clearGpuCache(); + } const tmpPath = `${settingsPath}.tmp`; fs.writeFileSync(tmpPath, JSON.stringify(completeSettings, null, 2), { mode: 0o600 }); fs.renameSync(tmpPath, settingsPath); @@ -319,7 +350,7 @@ export async function exportSettingsToFile( if (canceled || !filePath) return false; try { const settings = loadSettings(); - fs.writeFileSync(filePath, JSON.stringify(settings, null, 2)); + fs.writeFileSync(filePath, JSON.stringify(settings, null, 2), { mode: 0o600 }); return true; } catch (error) { log.error('Failed to export settings:', error); @@ -329,7 +360,7 @@ export async function exportSettingsToFile( export async function importSettingsFromFile( parentWindow: Electron.BrowserWindow | null -): Promise { +): Promise { if (!parentWindow || parentWindow.isDestroyed()) return false; const { canceled, filePaths } = await dialog.showOpenDialog(parentWindow, { title: 'Import Settings', @@ -357,7 +388,7 @@ export async function importSettingsFromFile( const tmpPath = `${settingsPath}.tmp`; fs.writeFileSync(tmpPath, JSON.stringify(migrated, null, 2), { mode: 0o600 }); fs.renameSync(tmpPath, settingsPath); - return true; + return migrated; } catch (error) { log.error('Failed to import settings:', error); return false; diff --git a/src/main/updater.ts b/src/main/updater.ts index 63fd5fa..a500762 100644 --- a/src/main/updater.ts +++ b/src/main/updater.ts @@ -69,10 +69,10 @@ export function compareVersions(a: string, b: string): number { return comparePrerelease(vA.prerelease, vB.prerelease); } -export function resolveUseBeta(channel: Settings['updateChannel']): boolean { +export function resolveUseBeta(channel: Settings['updateChannel'], appVersion?: string): boolean { if (channel === 'beta') return true; if (channel === 'stable') return false; - return false; + return isBetaVersion(appVersion || '0.0.0'); } export function applyChannel(useBeta: boolean) { @@ -93,7 +93,7 @@ export function setupAutoUpdater( autoUpdater.autoInstallOnAppQuit = true; const settings = loadSettings(); - const useBeta = resolveUseBeta(settings.updateChannel); + const useBeta = resolveUseBeta(settings.updateChannel, app.getVersion()); applyChannel(useBeta); const sendToWindow = (channel: 'updater-status' | 'updater-progress', data: unknown) => { @@ -116,7 +116,7 @@ export function setupAutoUpdater( }); autoUpdater.on('update-available', (info) => { - const currentUseBeta = resolveUseBeta(loadSettings().updateChannel); + const currentUseBeta = resolveUseBeta(loadSettings().updateChannel, app.getVersion()); const updateIsBeta = isBetaVersion(info.version); if (currentUseBeta && !updateIsBeta) { @@ -196,7 +196,7 @@ export async function checkForUpdates(isPackaged: boolean, loadSettings: () => S } try { const settings = loadSettings(); - const useBeta = resolveUseBeta(settings.updateChannel); + const useBeta = resolveUseBeta(settings.updateChannel, app.getVersion()); applyChannel(useBeta); return await autoUpdater.checkForUpdates(); } catch (error) { diff --git a/src/renderer/css/01-base.css b/src/renderer/css/01-base.css index 7b04d42..97da7f4 100644 --- a/src/renderer/css/01-base.css +++ b/src/renderer/css/01-base.css @@ -300,7 +300,14 @@ body { } body.animate-bg { - background: var(--bg-gradient-mid); + background: linear-gradient( + 135deg, + var(--bg-gradient-start), + var(--bg-gradient-mid), + var(--bg-gradient-end) + ); + background-size: 200% 200%; + animation: gradientShift 18s ease infinite; } .skip-link { @@ -328,6 +335,6 @@ body.animate-bg { background-position: 0% 50%; } 50% { - background-position: 0% 50%; + background-position: 100% 50%; } } diff --git a/src/renderer/css/02-sidebar.css b/src/renderer/css/02-sidebar.css index 7a76bcb..1a4ca95 100644 --- a/src/renderer/css/02-sidebar.css +++ b/src/renderer/css/02-sidebar.css @@ -323,6 +323,16 @@ select option { padding-left: var(--spacing-xs); } +.toggle-row { + display: flex; + align-items: center; + gap: var(--spacing-sm); +} + +.toggle-row .toggle-switch { + flex: 1; +} + .help-icon { display: inline-flex; align-items: center; diff --git a/src/renderer/css/04-panels.css b/src/renderer/css/04-panels.css index 26c173a..4580a7c 100644 --- a/src/renderer/css/04-panels.css +++ b/src/renderer/css/04-panels.css @@ -18,16 +18,30 @@ display: flex; align-items: center; justify-content: space-between; - padding: var(--spacing-sm) var(--spacing-md); + padding: 0 var(--spacing-md); background: var(--console-header-bg); border-bottom: 1px solid var(--border-subtle); +} + +.history-collapse-btn, +.console-collapse-btn { + flex: 1; + display: flex; + align-items: center; + min-height: 44px; + padding: var(--spacing-sm) 0; + background: transparent; + border: none; + color: inherit; cursor: pointer; user-select: none; + text-align: left; transition: var(--transition-fast); } -.history-header:hover { - background: var(--console-header-hover-bg); +.history-collapse-btn:hover, +.console-collapse-btn:hover { + color: var(--text-primary); } .history-header-left { @@ -229,16 +243,9 @@ display: flex; align-items: center; justify-content: space-between; - padding: var(--spacing-sm) var(--spacing-md); + padding: 0 var(--spacing-md); background: var(--console-header-bg); border-bottom: 1px solid var(--border-subtle); - cursor: pointer; - user-select: none; - transition: var(--transition-fast); -} - -.console-header:hover { - background: var(--console-header-hover-bg); } .console-header-left { diff --git a/src/renderer/css/05-overlays.css b/src/renderer/css/05-overlays.css index d79d44f..614f8b9 100644 --- a/src/renderer/css/05-overlays.css +++ b/src/renderer/css/05-overlays.css @@ -94,16 +94,28 @@ color: var(--text-primary); } -.modal-buttons button:first-child { +.modal-buttons .modal-btn-primary { background: var(--accent); border-color: transparent; color: var(--text-primary); } -.modal-buttons button:first-child:hover { +.modal-buttons .modal-btn-primary:hover { background: var(--accent-hover); } +.modal-buttons .modal-btn-danger { + background: var(--danger-subtle); + border-color: var(--danger); + color: var(--danger); +} + +.modal-buttons .modal-btn-danger:hover { + background: var(--danger); + border-color: var(--danger); + color: var(--text-primary); +} + .licenses-container { width: 90%; max-width: var(--licenses-max-width); @@ -267,6 +279,10 @@ pointer-events: none; } +.toast-container-assertive { + bottom: calc(var(--spacing-xl) + 80px); +} + .toast { pointer-events: auto; display: flex; @@ -394,7 +410,15 @@ .modal-close-btn:focus-visible, .sidebar-close:focus-visible, .console-clear-btn:focus-visible, -.clear-btn:focus-visible { +.clear-btn:focus-visible, +.preview-btn:focus-visible, +.paste-btn:focus-visible, +.queue-action-btn:focus-visible, +.history-open-btn:focus-visible, +.history-collapse-btn:focus-visible, +.console-collapse-btn:focus-visible, +.wizard-btn:focus-visible, +.history-clear-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: 0 0 0 4px var(--accent-subtle); diff --git a/src/renderer/css/06-responsive.css b/src/renderer/css/06-responsive.css index c019a74..f5914ba 100644 --- a/src/renderer/css/06-responsive.css +++ b/src/renderer/css/06-responsive.css @@ -21,6 +21,10 @@ } @media (max-width: 560px) { + .format-grid { + grid-template-columns: 1fr; + } + .url-input-wrapper { flex-direction: column; } diff --git a/src/renderer/css/07-queue.css b/src/renderer/css/07-queue.css index b1a5e53..f3788fe 100644 --- a/src/renderer/css/07-queue.css +++ b/src/renderer/css/07-queue.css @@ -134,6 +134,18 @@ border-color: var(--accent-hover); } +.queue-empty-message { + margin: 0; + padding: var(--spacing-sm) var(--spacing-md) var(--spacing-md); + color: var(--text-muted); + font-size: var(--text-xs); + text-align: center; +} + +.queue-section:not(.has-items) .queue-empty-message { + display: block; +} + .queue-list { max-height: 250px; overflow-y: auto; diff --git a/src/renderer/css/08-wizard.css b/src/renderer/css/08-wizard.css index ea47342..3d9169b 100644 --- a/src/renderer/css/08-wizard.css +++ b/src/renderer/css/08-wizard.css @@ -38,8 +38,14 @@ .wizard-theme-option input { position: absolute; - opacity: 0; - pointer-events: none; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; } .wizard-theme-card { diff --git a/src/renderer/index.html b/src/renderer/index.html index c49e8d8..83abce5 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -6,7 +6,24 @@ http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data: https:; connect-src 'self'; frame-src 'self'; object-src 'none'; base-uri 'self';" /> - Rosi + ROSI + @@ -199,14 +216,21 @@ Enhancements Embed thumbnail / cover art - - - - - SponsorBlock - ? - - + + + + + SponsorBlock + + ? + @@ -229,6 +253,12 @@ Enhancements Comma-separated language codes (for example en,es). Use all for every available language. + @@ -263,14 +293,21 @@ Browser Integration role="region" aria-labelledby="settingsSectionHeaderBrowser" > - - - - - Hook into web browser - ? - - + + + + + Hook into web browser + + ? + @@ -528,7 +565,13 @@ Application - + Download Videos Ready - + @@ -962,30 +1013,17 @@ Download Videos - - - - - - + + + Download Videos stroke-linecap="round" stroke-linejoin="round" > - - + - Recent Downloads + + + + + + Recent Downloads + + 0 - 0 - + Download Videos - - - - - - + + + Download Videos stroke-linecap="round" stroke-linejoin="round" > - - + - Console Output + + + + + + Console Output + - + Download Videos - + Downloading update… - + Download Videos aria-live="polite" aria-atomic="true" > - Cancel + + Cancel + @@ -1128,6 +1197,7 @@ Download Videos role="dialog" aria-modal="true" aria-labelledby="modal-title" + aria-hidden="true" > Modal Title @@ -1168,6 +1238,11 @@ Open Source Licenses + Open Source Licenses role="dialog" aria-modal="true" aria-labelledby="wizard-title" + aria-hidden="true" > diff --git a/src/renderer/licenses-iframe.html b/src/renderer/licenses-iframe.html index 3628d78..ceed0f6 100644 --- a/src/renderer/licenses-iframe.html +++ b/src/renderer/licenses-iframe.html @@ -2,7 +2,11 @@ - Rosi License & 3rd Party Licenses/Credits + + ROSI License & 3rd Party Licenses/Credits
en,es
all