diff --git a/README.md b/README.md index 6a622c7..65623b2 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Der Launcher konzentriert sich auf den Alltag: Instanz anlegen, Konto auswählen - Reimport überschreibt vorhandene Instanzen mit gleichem Namen statt Dubletten anzulegen - Export, Kopieren, Gruppieren und Verwalten von Instanzen - Mehrfachauswahl mit Strg-Klick für Sammellöschen mehrerer Instanzen +- Globales Backup und Wiederherstellung für alle Instanzen direkt im Einstellungsbereich - Verwaltung von Welten, Screenshots und Spielordnern direkt aus dem Launcher - Bedrock-Bereich für Start und Verwaltung vorhandener Bedrock-Daten unter Windows - Update-Funktion für neue Launcher-Versionen @@ -41,6 +42,8 @@ Für die meisten Nutzer ist die Setup-Version die bessere Wahl. Die portable Datei ist praktisch zum Mitnehmen, braucht beim Öffnen aber in der Regel etwas länger. +Automatische Updates bleiben dabei sauber getrennt: Die Setup-Version erhält nur Setup-Updates, die portable Variante nur Portable-Updates. Ein Wechsel zwischen beiden Varianten passiert nicht automatisch. + ## Voraussetzungen - Windows x64 @@ -71,6 +74,17 @@ Wenn bereits eine Instanz mit demselben Namen existiert, wird sie beim Import er Beim Import aus dem offiziellen Minecraft Launcher werden nur echte benutzerdefinierte Profile angezeigt. Standardprofile wie Latest Release oder Latest Snapshot werden ausgeblendet. Nutzt ein Profil den gemeinsamen Standard-Spielordner, übernimmt AeroMC daraus nur spielrelevante Daten wie Mods, Welten und Konfiguration statt den kompletten Launcher-Cache zu kopieren. +## Backup und Wiederherstellung + +Im Bereich Einstellungen > Backup können komplette Sicherungen aller Instanzen erstellt und wieder eingespielt werden. + +- Backups werden automatisch unter Dokumente\AeroMC Launcher\Backups gespeichert +- Gesichert werden alle Instanzen sowie die Launcher-Einstellungen +- Microsoft-Konten und Tokens werden bewusst nicht mitgesichert +- Vorhandene Backups werden im Wiederherstellen-Bereich direkt in einem Dropdown aufgelistet + +Wichtig: Beim Wiederherstellen werden alle aktuell vorhandenen Instanzen mit dem Inhalt des gewählten Backups überschrieben. Die Launcher-Einstellungen werden ebenfalls aus dem Backup übernommen. + ## Was AeroMC verwaltet - Minecraft-Versionen und gemeinsame Spieldateien diff --git a/package-lock.json b/package-lock.json index 503b462..9a55185 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,18 @@ { "name": "aeromc", - "version": "1.0.0-beta", + "version": "1.0.1-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aeromc", - "version": "1.0.0-beta", + "version": "1.0.1-beta", "license": "MIT", "dependencies": { + "archiver": "^7.0.1", "skinview3d": "^3.4.2", - "undici": "^6.28.0" + "undici": "^6.28.0", + "unzipper": "^0.12.5" }, "devDependencies": { "electron": "^28.0.0", @@ -312,7 +314,6 @@ "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", @@ -330,7 +331,6 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -343,7 +343,6 @@ "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" @@ -356,14 +355,12 @@ "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", @@ -381,7 +378,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.2.2" @@ -397,7 +393,6 @@ "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", @@ -493,7 +488,6 @@ "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": { @@ -685,6 +679,18 @@ "dev": true, "license": "MIT" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -729,7 +735,6 @@ "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" @@ -739,7 +744,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -853,82 +857,93 @@ } }, "node_modules/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", - "dev": true, + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", "license": "MIT", - "peer": true, "dependencies": { - "archiver-utils": "^2.1.0", + "archiver-utils": "^5.0.2", "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dev": true, + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", "license": "MIT", - "peer": true, "dependencies": { - "glob": "^7.1.4", + "glob": "^10.0.0", "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", + "lodash": "^4.17.15", "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, - "node_modules/archiver-utils/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", - "peer": true, + "node_modules/archiver-utils/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", + "license": "ISC", "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" + "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/archiver-utils/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", - "peer": true - }, - "node_modules/archiver-utils/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", - "peer": true, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", "dependencies": { - "safe-buffer": "~5.1.0" + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/archiver/node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" } }, "node_modules/argparse": { @@ -964,7 +979,6 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, "license": "MIT" }, "node_modules/async-exit-hook": { @@ -994,18 +1008,110 @@ "node": ">= 4.0.0" } }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "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/bare-events": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", + "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz", + "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", + "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", + "license": "Apache-2.0" + }, + "node_modules/bare-stream": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", + "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.6.tgz", + "integrity": "sha512-iQxPClE07hETVpbRoX7JXX3v/ZQViCxe/SYCxylRLzdEx1xJAufPptfiOqR8tqiCtmbtMDANKWszzjLu1PMAZQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, "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==", - "dev": true, "funding": [ { "type": "github", @@ -1035,11 +1141,26 @@ "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==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, "license": "MIT" }, "node_modules/bluebird-lst": { @@ -1065,7 +1186,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -1346,7 +1466,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -1359,7 +1478,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -1396,20 +1514,19 @@ } }, "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", - "dev": true, + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", "license": "MIT", - "peer": true, "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/concat-map": { @@ -1482,7 +1599,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, "license": "MIT" }, "node_modules/crc": { @@ -1500,9 +1616,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "crc32": "bin/crc32.njs" }, @@ -1511,25 +1625,22 @@ } }, "node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", - "dev": true, + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", "license": "MIT", - "peer": true, "dependencies": { "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -1803,11 +1914,49 @@ "node": ">= 0.4" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", + "dependencies": { + "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==", + "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==", + "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==", + "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": { @@ -1886,6 +2035,98 @@ "fs-extra": "^10.1.0" } }, + "node_modules/electron-builder-squirrel-windows/node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/archiver-utils/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", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -1916,6 +2157,59 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/electron-builder-squirrel-windows/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==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/electron-builder-squirrel-windows/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", + "peer": true + }, + "node_modules/electron-builder-squirrel-windows/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", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/electron-builder-squirrel-windows/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==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -1927,6 +2221,45 @@ "node": ">= 10.0.0" } }, + "node_modules/electron-builder-squirrel-windows/node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/electron-builder/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -2023,7 +2356,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/end-of-stream": { @@ -2134,6 +2466,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -2173,6 +2532,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2210,7 +2575,6 @@ "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", @@ -2511,7 +2875,6 @@ "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==", - "dev": true, "license": "ISC" }, "node_modules/has-flag": { @@ -2678,7 +3041,6 @@ "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", @@ -2711,7 +3073,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/is-ci": { @@ -2731,19 +3092,28 @@ "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/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "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", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/isbinaryfile": { "version": "5.0.7", @@ -2762,14 +3132,12 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/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" @@ -2888,9 +3256,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readable-stream": "^2.0.5" }, @@ -2902,9 +3268,7 @@ "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", - "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2919,17 +3283,13 @@ "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", - "peer": true + "license": "MIT" }, "node_modules/lazystream/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", - "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -2938,7 +3298,6 @@ "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "dev": true, "license": "MIT" }, "node_modules/lodash.defaults": { @@ -3084,7 +3443,6 @@ "version": "5.1.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -3107,7 +3465,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -3168,13 +3525,17 @@ "license": "MIT", "optional": true }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "license": "MIT" + }, "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, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -3227,7 +3588,6 @@ "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/path-is-absolute": { @@ -3244,7 +3604,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3254,7 +3613,6 @@ "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", @@ -3271,7 +3629,6 @@ "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/pend": { @@ -3303,13 +3660,20 @@ "node": ">=10.4.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "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==", - "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", @@ -3388,28 +3752,50 @@ } }, "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==", - "dev": true, + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "license": "MIT", - "peer": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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.2.1" } }, "node_modules/readdir-glob": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "minimatch": "^5.1.0" } @@ -3477,7 +3863,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -3492,8 +3877,7 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -3561,7 +3945,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -3574,7 +3957,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3584,7 +3966,6 @@ "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" @@ -3703,13 +4084,22 @@ "node": ">= 6" } }, + "node_modules/streamx": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", + "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "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==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -3718,7 +4108,6 @@ "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", @@ -3734,7 +4123,6 @@ "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", @@ -3749,7 +4137,6 @@ "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" @@ -3763,7 +4150,6 @@ "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" @@ -3818,21 +4204,24 @@ } }, "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==", - "dev": true, + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", "license": "MIT", - "peer": true, "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" } }, "node_modules/temp-file": { @@ -3884,6 +4273,15 @@ "node": ">= 10.0.0" } }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/three": { "version": "0.156.1", "resolved": "https://registry.npmjs.org/three/-/three-0.156.1.tgz", @@ -3974,6 +4372,54 @@ "node": ">= 4.0.0" } }, + "node_modules/unzipper": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.5.tgz", + "integrity": "sha512-tXYOi9R57Uj/2Z25SOs5RRSzq886MBQj2gY8dPL+xl/kv6s6SvByoKfAtvfVeEuhntWDgjd2o9p2lb4TVPAz0A==", + "license": "MIT", + "dependencies": { + "bluebird": "~3.7.2", + "duplexer2": "~0.1.4", + "fs-extra": "11.3.1", + "graceful-fs": "^4.2.2", + "node-int64": "^0.4.0" + } + }, + "node_modules/unzipper/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/unzipper/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/unzipper/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3995,9 +4441,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/verror": { "version": "1.10.1", @@ -4019,7 +4463,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -4054,7 +4497,6 @@ "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", @@ -4143,42 +4585,17 @@ } }, "node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", - "dev": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", "license": "MIT", - "peer": true, "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" + "node": ">= 14" } } } diff --git a/package.json b/package.json index 80bf517..88aa248 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "aeromc", "productName": "AeroMC", - "version": "1.0.0-beta", + "version": "1.0.1-beta", "description": "AeroMC – Instanz-basierter Minecraft-Launcher (à la MultiMC/Prism)", "homepage": "https://M_Viper.de", "main": "src/main.js", @@ -39,7 +39,9 @@ "electron-builder": "^24.0.0" }, "dependencies": { + "archiver": "^7.0.1", "skinview3d": "^3.4.2", - "undici": "^6.28.0" + "undici": "^6.28.0", + "unzipper": "^0.12.5" } } diff --git a/src/backup.js b/src/backup.js new file mode 100644 index 0000000..686e735 --- /dev/null +++ b/src/backup.js @@ -0,0 +1,440 @@ +'use strict'; + +const archiver = require('archiver'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { pipeline } = require('stream/promises'); +const unzipper = require('unzipper'); + +function pad2(value) { + return String(value).padStart(2, '0'); +} + +function defaultBackupFileName(date = new Date()) { + return 'AeroMC-Backup-' + + date.getFullYear() + '-' + + pad2(date.getMonth() + 1) + '-' + + pad2(date.getDate()) + '_' + + pad2(date.getHours()) + '-' + + pad2(date.getMinutes()) + '-' + + pad2(date.getSeconds()) + + '.zip'; +} + +function defaultBackupDir(documentsDir) { + return path.join(documentsDir, 'AeroMC Launcher', 'Backups'); +} + +function resolveAutoBackupPath(documentsDir, date = new Date()) { + const dir = defaultBackupDir(documentsDir); + ensureDir(dir); + + const baseName = defaultBackupFileName(date); + const ext = path.extname(baseName); + const stem = baseName.slice(0, -ext.length); + let attempt = 0; + let candidate = path.join(dir, baseName); + + while (fs.existsSync(candidate)) { + attempt += 1; + candidate = path.join(dir, `${stem}-${attempt}${ext}`); + } + + return candidate; +} + +function listGlobalBackups(documentsDir) { + const dir = defaultBackupDir(documentsDir); + ensureDir(dir); + + return fs.readdirSync(dir, { withFileTypes: true }) + .filter((entry) => entry.isFile() && /\.zip$/i.test(entry.name) && !/\.partial\.zip$/i.test(entry.name)) + .map((entry) => { + const filePath = path.join(dir, entry.name); + const stat = fs.statSync(filePath); + return { + name: entry.name, + path: filePath, + size: stat.size, + modifiedAt: stat.mtime.toISOString(), + }; + }) + .sort((a, b) => { + const timeDiff = new Date(b.modifiedAt).getTime() - new Date(a.modifiedAt).getTime(); + return timeDiff || a.name.localeCompare(b.name, 'de'); + }); +} + +function ensureDir(dir) { + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); +} + +function countInstanceDirs(instancesDir) { + if (!fs.existsSync(instancesDir)) return 0; + return fs.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).length; +} + +function readJson(file, fallback) { + try { + return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '')); + } catch { + return fallback; + } +} + +function writeJson(file, data) { + ensureDir(path.dirname(file)); + fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8'); +} + +function emitProgress(cb, mode, percent, detail) { + if (!cb) return; + cb({ mode, percent: Math.max(0, Math.min(100, Math.round(percent))), detail: detail || '' }); +} + +function normalizeZipEntryPath(entryPath) { + const parts = String(entryPath || '') + .replace(/\\/g, '/') + .split('/') + .filter(Boolean); + + if (!parts.length) return ''; + if (parts.some((part) => part === '.' || part === '..')) { + throw new Error('Das Backup-Archiv enthält ungültige Pfade.'); + } + + return path.join(...parts); +} + +async function extractBackupArchive(sourcePath, destinationDir, options) { + const opts = options || {}; + const onProgress = opts.onProgress; + const mode = opts.mode || 'restore'; + const startPercent = opts.startPercent ?? 5; + const endPercent = opts.endPercent ?? 18; + const detail = opts.detail || 'Entpacke Backup'; + const directory = await unzipper.Open.file(sourcePath); + const files = directory.files || []; + const fileEntries = files.filter((entry) => entry.type !== 'Directory'); + const totalBytes = fileEntries.reduce((sum, entry) => sum + Number(entry.uncompressedSize || 0), 0); + let processedBytes = 0; + let processedEntries = 0; + + ensureDir(destinationDir); + if (!files.length) { + emitProgress(onProgress, mode, endPercent, detail + ' abgeschlossen'); + return; + } + + for (const entry of files) { + const relativePath = normalizeZipEntryPath(entry.path); + if (!relativePath) continue; + + const targetPath = path.join(destinationDir, relativePath); + if (entry.type === 'Directory') { + ensureDir(targetPath); + continue; + } + + ensureDir(path.dirname(targetPath)); + await pipeline(entry.stream(), fs.createWriteStream(targetPath)); + + processedBytes += Number(entry.uncompressedSize || 0); + processedEntries += 1; + const ratio = totalBytes > 0 + ? Math.max(0, Math.min(1, processedBytes / totalBytes)) + : Math.max(0, Math.min(1, processedEntries / Math.max(1, fileEntries.length))); + const percent = startPercent + ((endPercent - startPercent) * ratio); + emitProgress(onProgress, mode, percent, detail + ' ...'); + } +} + +function createBackupArchive(options) { + const opts = options || {}; + const instancesDir = opts.instancesDir; + const settingsFile = opts.settingsFile; + const includeSettings = opts.includeSettings !== false; + const backupInfo = opts.backupInfo || {}; + const destinationPath = opts.destinationPath; + const onProgress = opts.onProgress; + const mode = opts.mode || 'create'; + const startPercent = opts.startPercent ?? 15; + const endPercent = opts.endPercent ?? 98; + const detail = opts.detail || 'Packe ZIP-Archiv'; + + return new Promise((resolve, reject) => { + ensureDir(path.dirname(destinationPath)); + + const output = fs.createWriteStream(destinationPath); + const archive = archiver('zip', { store: true }); + let done = false; + + const fail = (err) => { + if (done) return; + done = true; + try { archive.destroy(); } catch { /* ignore */ } + try { output.destroy(); } catch { /* ignore */ } + reject(err); + }; + + output.on('close', () => { + if (done) return; + done = true; + resolve({ bytes: archive.pointer() }); + }); + output.on('error', fail); + archive.on('error', fail); + archive.on('warning', (err) => { + if (err && err.code === 'ENOENT') return; + fail(err); + }); + archive.on('progress', (progress) => { + const totalBytes = progress && progress.fs ? progress.fs.totalBytes : 0; + const processedBytes = progress && progress.fs ? progress.fs.processedBytes : 0; + if (!totalBytes) return; + + const ratio = Math.max(0, Math.min(1, processedBytes / totalBytes)); + const percent = startPercent + ((endPercent - startPercent) * ratio); + emitProgress(onProgress, mode, percent, detail + ' ...'); + }); + + archive.pipe(output); + if (fs.existsSync(instancesDir)) archive.directory(instancesDir, 'instances'); + else archive.append('', { name: 'instances/.keep' }); + + if (includeSettings && settingsFile && fs.existsSync(settingsFile)) { + archive.file(settingsFile, { name: 'settings.json' }); + } + + archive.append(`${JSON.stringify(backupInfo, null, 2)}\n`, { name: 'backup-info.json' }); + + try { + const finalizeResult = archive.finalize(); + if (finalizeResult && typeof finalizeResult.catch === 'function') finalizeResult.catch(fail); + } catch (err) { + fail(err); + } + }); +} + +function copyDirContentsTracked(sourceDir, targetDir, options) { + const opts = options || {}; + const mode = opts.mode || 'create'; + const onProgress = opts.onProgress; + const startPercent = opts.startPercent ?? 0; + const endPercent = opts.endPercent ?? 100; + const detailPrefix = opts.detailPrefix || 'Kopiere'; + + ensureDir(targetDir); + if (!fs.existsSync(sourceDir)) { + emitProgress(onProgress, mode, endPercent, detailPrefix + ' abgeschlossen'); + return; + } + + const entries = fs.readdirSync(sourceDir, { withFileTypes: true }); + if (!entries.length) { + emitProgress(onProgress, mode, endPercent, detailPrefix + ' abgeschlossen'); + return; + } + + entries.forEach((entry, index) => { + fs.cpSync(path.join(sourceDir, entry.name), path.join(targetDir, entry.name), { recursive: true }); + const ratio = (index + 1) / entries.length; + const percent = startPercent + ((endPercent - startPercent) * ratio); + emitProgress(onProgress, mode, percent, `${detailPrefix}: ${entry.name}`); + }); +} + +function moveDirTracked(sourceDir, targetDir, options) { + const opts = options || {}; + const mode = opts.mode || 'restore'; + const onProgress = opts.onProgress; + const startPercent = opts.startPercent ?? 0; + const endPercent = opts.endPercent ?? 100; + const detailPrefix = opts.detailPrefix || 'Verschiebe'; + + if (!fs.existsSync(sourceDir)) { + emitProgress(onProgress, mode, endPercent, detailPrefix + ' abgeschlossen'); + return; + } + + ensureDir(path.dirname(targetDir)); + try { + fs.renameSync(sourceDir, targetDir); + emitProgress(onProgress, mode, endPercent, detailPrefix + ' abgeschlossen'); + return; + } catch (err) { + if (!err || !['EXDEV', 'EPERM', 'EACCES'].includes(err.code)) throw err; + } + + copyDirContentsTracked(sourceDir, targetDir, opts); + try { fs.rmSync(sourceDir, { recursive: true, force: true }); } catch { /* ignore */ } +} + +function resolveExtractedBackupRoot(extractDir) { + if (fs.existsSync(path.join(extractDir, 'instances'))) return extractDir; + const entries = fs.readdirSync(extractDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()); + if (entries.length === 1) { + const nested = path.join(extractDir, entries[0].name); + if (fs.existsSync(path.join(nested, 'instances'))) return nested; + } + return null; +} + +async function createGlobalBackup(options) { + const instancesDir = options && options.instancesDir; + const userDataDir = options && options.userDataDir; + const destinationPath = options && options.destinationPath; + const includeSettings = !options || options.includeSettings !== false; + const onProgress = options && options.onProgress; + if (!instancesDir || !userDataDir || !destinationPath) throw new Error('Backup-Parameter unvollständig.'); + + const settingsFile = path.join(userDataDir, 'settings.json'); + const workingArchivePath = destinationPath.replace(/\.zip$/i, '') + '.partial.zip'; + const backupInfo = { + createdAt: new Date().toISOString(), + instanceCount: countInstanceDirs(instancesDir), + includeSettings, + app: 'AeroMC', + version: 1, + }; + + try { + emitProgress(onProgress, 'create', 5, 'Bereite Backup vor'); + emitProgress(onProgress, 'create', 10, 'Ermittle Backup-Inhalt'); + + if (fs.existsSync(destinationPath)) fs.unlinkSync(destinationPath); + if (fs.existsSync(workingArchivePath)) fs.unlinkSync(workingArchivePath); + emitProgress(onProgress, 'create', 90, 'Packe ZIP-Archiv'); + await createBackupArchive({ + instancesDir, + settingsFile, + includeSettings, + backupInfo, + destinationPath: workingArchivePath, + mode: 'create', + onProgress, + startPercent: 15, + endPercent: 98, + detail: 'Packe ZIP-Archiv', + }); + emitProgress(onProgress, 'create', 99, 'Finalisiere Backup'); + fs.renameSync(workingArchivePath, destinationPath); + emitProgress(onProgress, 'create', 100, 'Backup abgeschlossen'); + + return { + ok: true, + path: destinationPath, + instanceCount: countInstanceDirs(instancesDir), + includedSettings: includeSettings && fs.existsSync(settingsFile), + }; + } finally { + try { + if (fs.existsSync(workingArchivePath)) fs.rmSync(workingArchivePath, { force: true }); + } catch { /* ignore */ } + } +} + +async function restoreGlobalBackup(options) { + const instancesDir = options && options.instancesDir; + const userDataDir = options && options.userDataDir; + const sourcePath = options && options.sourcePath; + const includeSettings = !!(options && options.includeSettings); + const onProgress = options && options.onProgress; + const instancesDirSetting = options && Object.prototype.hasOwnProperty.call(options, 'instancesDirSetting') + ? options.instancesDirSetting + : null; + if (!instancesDir || !userDataDir || !sourcePath) throw new Error('Restore-Parameter unvollständig.'); + + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'aeromc-restore-')); + const extractDir = path.join(tmp, 'extract'); + const rollbackDir = path.join(tmp, 'rollback'); + const rollbackInstances = path.join(rollbackDir, 'instances'); + const settingsFile = path.join(userDataDir, 'settings.json'); + const rollbackSettings = path.join(rollbackDir, 'settings.json'); + let settingsPreviouslyExisted = false; + + try { + ensureDir(extractDir); + emitProgress(onProgress, 'restore', 5, 'Entpacke Backup'); + await extractBackupArchive(sourcePath, extractDir, { + mode: 'restore', + onProgress, + startPercent: 5, + endPercent: 18, + detail: 'Entpacke Backup', + }); + + const backupRoot = resolveExtractedBackupRoot(extractDir); + if (!backupRoot) throw new Error('Das Archiv enthält kein gültiges AeroMC-Backup.'); + emitProgress(onProgress, 'restore', 18, 'Backup geprüft'); + + const sourceInstances = path.join(backupRoot, 'instances'); + const sourceSettings = path.join(backupRoot, 'settings.json'); + ensureDir(rollbackDir); + + emitProgress(onProgress, 'restore', 25, 'Sichere aktuellen Stand'); + moveDirTracked(instancesDir, rollbackInstances, { + mode: 'restore', + onProgress, + startPercent: 28, + endPercent: 40, + detailPrefix: 'Sichere aktuelle Instanzen', + }); + settingsPreviouslyExisted = fs.existsSync(settingsFile); + if (settingsPreviouslyExisted) fs.copyFileSync(settingsFile, rollbackSettings); + + ensureDir(path.dirname(instancesDir)); + moveDirTracked(sourceInstances, instancesDir, { + mode: 'restore', + onProgress, + startPercent: 45, + endPercent: 78, + detailPrefix: 'Stelle Instanzen wieder her', + }); + + let restoredSettings = false; + if (includeSettings && fs.existsSync(sourceSettings)) { + const restored = readJson(sourceSettings, {}); + restored.instancesDir = instancesDirSetting; + writeJson(settingsFile, restored); + restoredSettings = true; + emitProgress(onProgress, 'restore', 90, 'Stelle Einstellungen wieder her'); + } else { + emitProgress(onProgress, 'restore', 90, 'Einstellungen übersprungen'); + } + + emitProgress(onProgress, 'restore', 100, 'Wiederherstellen abgeschlossen'); + + return { + ok: true, + path: sourcePath, + instanceCount: countInstanceDirs(instancesDir), + restoredSettings, + }; + } catch (err) { + try { + if (fs.existsSync(instancesDir)) fs.rmSync(instancesDir, { recursive: true, force: true }); + if (fs.existsSync(rollbackInstances)) fs.cpSync(rollbackInstances, instancesDir, { recursive: true }); + else ensureDir(instancesDir); + + if (includeSettings) { + if (fs.existsSync(rollbackSettings)) fs.copyFileSync(rollbackSettings, settingsFile); + else if (!settingsPreviouslyExisted && fs.existsSync(settingsFile)) fs.rmSync(settingsFile, { force: true }); + } + } catch { /* ignore rollback errors */ } + throw err; + } finally { + try { fs.rmSync(tmp, { recursive: true, force: true }); } catch { /* ignore */ } + } +} + +module.exports = { + createGlobalBackup, + restoreGlobalBackup, + defaultBackupFileName, + defaultBackupDir, + listGlobalBackups, + resolveAutoBackupPath, +}; \ No newline at end of file diff --git a/src/cfimport.js b/src/cfimport.js new file mode 100644 index 0000000..e84f483 --- /dev/null +++ b/src/cfimport.js @@ -0,0 +1,196 @@ +'use strict'; + +/* + * cfimport.js – Instanzen aus dem CurseForge-Launcher übernehmen + * --------------------------------------------------------------- + * Unterstützt sowohl den Launcher-Wurzelordner als auch direkt den + * Instances-Ordner. Gelesen werden nach Möglichkeit minecraftinstance.json + * und die vorhandenen Spieldaten im Profilordner. + */ + +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +const META_FILES = ['minecraftinstance.json', 'instance.json']; + +function readJson(file, fallback) { + try { + return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '')); + } catch { + return fallback; + } +} + +function existingMetaFile(dir) { + return META_FILES.map((name) => path.join(dir, name)).find((file) => fs.existsSync(file)) || null; +} + +function metaVersion(meta) { + return String( + (meta && ( + meta.gameVersion || meta.minecraftVersion || meta.mcVersion || + (meta.installedModpack && meta.installedModpack.gameVersion) + )) || '' + ).trim(); +} + +function resolveInstancesDir(inputPath) { + if (!inputPath || !fs.existsSync(inputPath)) return null; + const directNames = new Set(['instances', 'minecraftinstances']); + if (directNames.has(path.basename(inputPath).toLowerCase())) return inputPath; + + const candidates = [ + path.join(inputPath, 'Instances'), + path.join(inputPath, 'instances'), + path.join(inputPath, 'minecraftInstances'), + path.join(inputPath, 'Minecraft', 'Instances'), + path.join(inputPath, 'minecraft', 'Instances'), + ]; + + const looksLikeWindowsInstall = + /curseforge windows/i.test(inputPath) || + (fs.existsSync(path.join(inputPath, 'CurseForge.exe')) && fs.existsSync(path.join(inputPath, 'resources'))); + if (looksLikeWindowsInstall) { + candidates.push( + path.join(os.homedir(), 'curseforge', 'minecraft', 'Instances'), + path.join(os.homedir(), 'CurseForge', 'minecraft', 'Instances'), + ); + } + return candidates.find((candidate) => fs.existsSync(candidate)) || null; +} + +function normalizeLoaderVersion(loader, rawValue, mcVersion) { + let value = String(rawValue || '').trim(); + if (!value) return ''; + + if (loader === 'forge') { + value = value.replace(/^forge-/i, ''); + if (mcVersion && value.startsWith(mcVersion + '-')) value = value.slice(mcVersion.length + 1); + return value; + } + if (loader === 'neoforge') { + return value.replace(/^neoforge-/i, ''); + } + if (loader === 'fabric') { + value = value.replace(/^fabric(?:-loader)?-/i, ''); + if (mcVersion && value.endsWith('-' + mcVersion)) value = value.slice(0, -1 * (mcVersion.length + 1)); + return value; + } + if (loader === 'quilt') { + value = value.replace(/^quilt(?:-loader)?-/i, ''); + if (mcVersion && value.endsWith('-' + mcVersion)) value = value.slice(0, -1 * (mcVersion.length + 1)); + return value; + } + return value; +} + +function detectLoader(meta, mcVersion) { + const raw = meta && ( + meta.baseModLoader || meta.modLoader || meta.modloader || meta.loader || meta.modLoaderId + ); + const value = typeof raw === 'object' + ? (raw.name || raw.id || raw.value || raw.version || '') + : (raw || ''); + const lower = String(value).toLowerCase(); + const objectVersion = raw && typeof raw === 'object' + ? (raw.version || raw.name || raw.id || '') + : ''; + + if (!lower) return { loader: 'vanilla', loaderVersion: '' }; + if (lower.includes('neoforge')) return { loader: 'neoforge', loaderVersion: normalizeLoaderVersion('neoforge', objectVersion || value, mcVersion) }; + if (lower.includes('fabric')) return { loader: 'fabric', loaderVersion: normalizeLoaderVersion('fabric', objectVersion || value, mcVersion) }; + if (lower.includes('quilt')) return { loader: 'quilt', loaderVersion: normalizeLoaderVersion('quilt', objectVersion || value, mcVersion) }; + if (lower.includes('forge')) return { loader: 'forge', loaderVersion: normalizeLoaderVersion('forge', objectVersion || value, mcVersion) }; + return { loader: 'vanilla', loaderVersion: '' }; +} + +function detectVersion(meta, loaderVersion) { + return metaVersion(meta) || (loaderVersion && loaderVersion.includes('-') ? loaderVersion.split('-')[0] : ''); +} + +function findGameDir(dir) { + const nested = ['minecraft', '.minecraft'] + .map((name) => path.join(dir, name)) + .find((candidate) => fs.existsSync(candidate)); + if (nested) return nested; + + const markers = ['mods', 'config', 'resourcepacks', 'shaderpacks', 'saves', 'options.txt']; + return markers.some((name) => fs.existsSync(path.join(dir, name))) ? dir : null; +} + +function scan(inputPath) { + const instancesDir = resolveInstancesDir(inputPath); + if (!instancesDir) return { ok: false, reason: 'not-found' }; + + const list = []; + for (const entry of fs.readdirSync(instancesDir, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const dir = path.join(instancesDir, entry.name); + const metaFile = existingMetaFile(dir); + const meta = metaFile ? readJson(metaFile, {}) : {}; + const gameDir = findGameDir(dir); + if (!metaFile && !gameDir) continue; + + const versionHint = metaVersion(meta); + const loaderInfo = detectLoader(meta, versionHint); + list.push({ + folder: entry.name, + dir, + name: meta.name || meta.displayName || entry.name, + group: '', + notes: meta.notes || meta.summary || '', + version: versionHint || detectVersion(meta, loaderInfo.loaderVersion), + loader: loaderInfo.loader, + loaderVersion: loaderInfo.loaderVersion, + javaPath: String(meta.javaPath || meta.javaExecutable || '').trim(), + minMemMb: Number(meta.minimumMemory || meta.minMemory || meta.minMemAlloc) || null, + maxMemMb: Number(meta.maximumMemory || meta.maxMemory || meta.maxMemAlloc || meta.allocatedMemory) || null, + gameDir: gameDir || dir, + hasGameData: !!gameDir, + }); + } + list.sort((a, b) => a.name.localeCompare(b.name)); + return { ok: true, instancesDir, count: list.length, instances: list }; +} + +function copyInto(sourceDir, targetDir) { + fs.mkdirSync(targetDir, { recursive: true }); + for (const name of fs.readdirSync(sourceDir)) { + if (META_FILES.includes(name)) continue; + fs.cpSync(path.join(sourceDir, name), path.join(targetDir, name), { recursive: true }); + } +} + +function importOne(store, entry, copyData = true) { + const created = store.createOrReplaceImportedInstance({ + name: entry.name, + group: entry.group, + notes: entry.notes, + minecraft: { + version: entry.version, + loader: entry.loader, + loaderVersion: entry.loaderVersion, + }, + }); + + const patch = {}; + if (entry.javaPath || entry.minMemMb || entry.maxMemMb) { + patch.java = { + path: entry.javaPath || '', + minMemMb: entry.minMemMb || null, + maxMemMb: entry.maxMemMb || null, + extraArgs: '', + }; + } + if (Object.keys(patch).length) store.updateInstance(created.id, patch); + + let copied = false; + if (copyData && entry.gameDir && fs.existsSync(entry.gameDir)) { + copyInto(entry.gameDir, store.gameDir(created.id)); + copied = true; + } + return { id: created.id, name: created.name, copied }; +} + +module.exports = { scan, importOne, resolveInstancesDir }; \ No newline at end of file diff --git a/src/index.html b/src/index.html index 009de2f..abebfb5 100644 --- a/src/index.html +++ b/src/index.html @@ -11,8 +11,7 @@
- - + @@ -192,6 +191,7 @@ +
@@ -229,12 +229,6 @@
Hier werden alle Instanzen als Unterordner mit ihrem Namen gespeichert. - -
@@ -426,6 +420,44 @@ + +