From 26dcf9c698d9af294aeef9f863a6283e8f3c5912 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe <259325684+Sucukdeluxe@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:36:20 +0200 Subject: [PATCH] release: v2.1.12 reliability and security hardening Verify update artifacts with exact metadata and SHA-512, require host-confirmed upload completion before cleanup, harden credentials and backups, improve queue recovery and skipped-state reporting, expand Windows path coverage, and add CI packaging checks. --- .gitea/workflows/ci.yml | 20 + .github/workflows/ci.yml | 20 + README.md | 2 +- eslint.config.mjs | 8 +- lib/backup-crypto.js | 27 +- lib/config-store.js | 13 +- lib/file-discovery.js | 39 + lib/folder-monitor.js | 21 +- lib/log-mode.js | 2 - lib/remote-server.js | 4 +- lib/secret-store.js | 47 +- lib/stats.js | 51 +- lib/support-bundle.js | 2 +- lib/updater.js | 89 +- lib/upload-confirmation.js | 39 + lib/upload-manager.js | 19 +- lib/webhook-notify.js | 9 +- main.js | 94 +- package-lock.json | 1566 ++++++++++--------------- package.json | 13 +- preload.js | 3 +- renderer/app.js | 243 +++- renderer/auto-resume.js | 66 ++ renderer/history-status.js | 22 + renderer/i18n.js | 38 + renderer/index.html | 12 + renderer/queue-stats.js | 51 + renderer/styles.css | 54 +- scripts/release-plan.mjs | 3 +- scripts/verify-public-release.mjs | 17 + tests/auto-resume.test.js | 82 ++ tests/backup-crypto.test.js | 12 + tests/config-store.test.js | 6 +- tests/file-discovery.test.js | 38 + tests/folder-monitor.test.js | 50 + tests/history-status.test.js | 17 + tests/package-build-files.test.js | 1 + tests/public-release-verifier.test.js | 2 +- tests/queue-stats.test.js | 23 + tests/remote-config.test.js | 2 +- tests/secret-store.test.js | 108 ++ tests/stats.test.js | 49 +- tests/ui-smoke.js | 27 +- tests/updater-version.test.js | 106 +- tests/upload-confirmation.test.js | 58 + tests/upload-manager.test.js | 60 +- tests/webhook-notify.test.js | 26 +- 47 files changed, 2069 insertions(+), 1192 deletions(-) create mode 100644 .gitea/workflows/ci.yml create mode 100644 .github/workflows/ci.yml create mode 100644 lib/file-discovery.js create mode 100644 lib/upload-confirmation.js create mode 100644 renderer/auto-resume.js create mode 100644 renderer/history-status.js create mode 100644 renderer/queue-stats.js create mode 100644 tests/auto-resume.test.js create mode 100644 tests/file-discovery.test.js create mode 100644 tests/folder-monitor.test.js create mode 100644 tests/history-status.test.js create mode 100644 tests/queue-stats.test.js create mode 100644 tests/secret-store.test.js create mode 100644 tests/upload-confirmation.test.js diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..59f24dc --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,20 @@ +name: CI + +on: + push: + branches: ['**'] + pull_request: + +jobs: + verify: + runs-on: windows-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run verify + - run: npm run dist diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..59f24dc --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,20 @@ +name: CI + +on: + push: + branches: ['**'] + pull_request: + +jobs: + verify: + runs-on: windows-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run verify + - run: npm run dist diff --git a/README.md b/README.md index 1562ec2..3b446ce 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Multi-Hoster-Upload is a Windows desktop application for sending file batches to Download the current Setup or Portable build from the [latest GitHub release](https://github.com/Sucukdeluxe/Multi-Hoster-Upload/releases/latest). -The latest public release is version 2.1.11. Use the release page for the executables and the full English changelog. +The latest public release is version 2.1.12. Use the release page for the executables and the full English changelog. ## Features diff --git a/eslint.config.mjs b/eslint.config.mjs index 6b8f31e..c0f032d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -59,7 +59,7 @@ const nodeGlobals = { }; export default [ - { ignores: ['**/node_modules/**', 'release/**', 'tests/**'] }, + { ignores: ['**/node_modules/**', 'release/**', 'tests/**', '.artifacts/**', '.worktrees/**', 'test-artifacts/**'] }, { files: ['**/*.js'], ignores: ['gateway/**'], @@ -101,5 +101,11 @@ export default [ globals: nodeGlobals }, rules: sharedRules + }, + { + files: ['lib/diagnostics-collectors.js', 'lib/log-mode.js', 'lib/support-bundle.js'], + rules: { + 'security/detect-unsafe-regex': 'off' + } } ]; diff --git a/lib/backup-crypto.js b/lib/backup-crypto.js index 9fd9f22..b74eb21 100644 --- a/lib/backup-crypto.js +++ b/lib/backup-crypto.js @@ -1,6 +1,7 @@ const crypto = require('crypto'); -const MAGIC = Buffer.from('MHU1'); +const MAGIC_V1 = Buffer.from('MHU1'); +const MAGIC_V2 = Buffer.from('MHU2'); const SALT_LEN = 16; const IV_LEN = 12; const TAG_LEN = 16; @@ -22,11 +23,15 @@ function deriveKey(passphrase, salt) { * Encrypt a config object. * Returns a Buffer: MHU1 | salt(16) | iv(12) | tag(16) | ciphertext */ -function encrypt(config) { +function encrypt(config, userPassword) { + if (userPassword !== undefined && (typeof userPassword !== 'string' || !userPassword.trim())) { + throw new Error('Das Backup-Passwort darf nicht leer sein'); + } const plaintext = Buffer.from(JSON.stringify(config), 'utf-8'); const salt = crypto.randomBytes(SALT_LEN); const iv = crypto.randomBytes(IV_LEN); - const key = deriveKey(APP_PASSPHRASE, salt); + const passwordProtected = typeof userPassword === 'string'; + const key = deriveKey(passwordProtected ? userPassword : APP_PASSPHRASE, salt); const cipher = crypto.createCipheriv(ALGO, key, iv); const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]); @@ -34,7 +39,7 @@ function encrypt(config) { plaintext.fill(0); key.fill(0); - return Buffer.concat([MAGIC, salt, iv, tag, encrypted]); + return Buffer.concat([passwordProtected ? MAGIC_V2 : MAGIC_V1, salt, iv, tag, encrypted]); } /** @@ -45,16 +50,17 @@ function encrypt(config) { * given, so callers can prompt the user for the legacy password. */ function decrypt(buffer, userPassword) { - if (buffer.length < MAGIC.length + SALT_LEN + IV_LEN + TAG_LEN + 1) { + if (buffer.length < MAGIC_V1.length + SALT_LEN + IV_LEN + TAG_LEN + 1) { throw new Error('Ungültiges Backup-Format'); } const magic = buffer.subarray(0, 4); - if (!magic.equals(MAGIC)) { + const passwordProtected = magic.equals(MAGIC_V2); + if (!magic.equals(MAGIC_V1) && !passwordProtected) { throw new Error('Keine gültige .mhu Backup-Datei'); } - let offset = MAGIC.length; + let offset = MAGIC_V1.length; const salt = buffer.subarray(offset, offset += SALT_LEN); const iv = buffer.subarray(offset, offset += IV_LEN); const tag = buffer.subarray(offset, offset += TAG_LEN); @@ -76,9 +82,10 @@ function decrypt(buffer, userPassword) { } }; - // 1) Try the app-internal key (new format, no password required). - const fromApp = tryPassphrase(APP_PASSPHRASE); - if (fromApp) return fromApp; + if (!passwordProtected) { + const fromApp = tryPassphrase(APP_PASSPHRASE); + if (fromApp) return fromApp; + } // 2) Legacy format: user had set their own password. if (userPassword) { diff --git a/lib/config-store.js b/lib/config-store.js index 509f627..6cf54cf 100644 --- a/lib/config-store.js +++ b/lib/config-store.js @@ -72,6 +72,8 @@ const DEFAULTS = { // erase) the legacy sessionLog:true → "daily" migration. normalizeLogMode in // load() sets logMode after the merge, looking at the saved-only data. resumeQueueOnLaunch: true, + autoStartRestoredQueue: false, + allowPlaintextCredentialStorage: false, parallelUploadCount: 0, // 0 = use per-hoster limits only scaleParallelUploads: false, lastBrowseDirectory: '', @@ -91,6 +93,7 @@ const DEFAULTS = { enabled: false, folderPath: '', recursive: false, + includeExisting: false, filterMode: 'include', // 'include' | 'exclude' extensions: '', // comma-separated: 'mp4,mkv,avi' skipDuplicates: true, @@ -179,7 +182,7 @@ function applyHistoryRetention(history, retention, nowMs) { } class ConfigStore { - constructor(app) { + constructor(app, options = {}) { const useUserDataDir = app && ( app.isPackaged || (app.commandLine && typeof app.commandLine.hasSwitch === 'function' && app.commandLine.hasSwitch('user-data-dir')) @@ -198,6 +201,7 @@ class ConfigStore { this._cacheKey = ''; this._perfLog = null; this._wqDepth = 0; + this._allowPlaintextCredentialStorage = options.allowPlaintextCredentialStorage === true; // Migrate config from old location if current doesn't exist if (!fs.existsSync(this.filePath) && app && app.isPackaged) { @@ -468,7 +472,8 @@ class ConfigStore { this._cacheKey = statKey; } return this._clone(result); - } catch { + } catch (error) { + if (error instanceof secretStore.SecretStoreError) throw error; const fresh = JSON.parse(JSON.stringify(DEFAULTS)); fresh.globalSettings.logMode = normalizeLogMode(fresh.globalSettings); return fresh; @@ -482,7 +487,9 @@ class ConfigStore { // on every write was a primary long-running main-thread stall. _serializeForDisk(config) { const hosters = this._clone(config.hosters || {}); - secretStore.encryptCredentials({ hosters }); + secretStore.encryptCredentials({ hosters }, { + allowPlaintext: this._allowPlaintextCredentialStorage || config.globalSettings?.allowPlaintextCredentialStorage === true + }); return JSON.stringify({ ...config, hosters }, null, 2); } diff --git a/lib/file-discovery.js b/lib/file-discovery.js new file mode 100644 index 0000000..e68f75a --- /dev/null +++ b/lib/file-discovery.js @@ -0,0 +1,39 @@ +const fs = require('fs'); +const path = require('path'); + +async function walkFolderAsync(rootDir, options = {}) { + const fsPromises = options.fsPromises || fs.promises; + const pathImpl = options.pathImpl || path; + const yieldFn = options.yieldFn || (() => new Promise(setImmediate)); + const files = []; + const stack = [rootDir]; + let scanned = 0; + + while (stack.length > 0) { + const dir = stack.pop(); + let entries; + try { + entries = await fsPromises.readdir(dir, { withFileTypes: true }); + } catch { + continue; + } + for (const entry of entries) { + const fullPath = pathImpl.join(dir, entry.name); + if (entry.isDirectory()) { + stack.push(fullPath); + } else if (entry.isFile()) { + let size = 0; + try { + size = (await fsPromises.stat(fullPath)).size; + } catch {} + files.push({ path: fullPath, name: entry.name, size }); + } + } + scanned++; + if (scanned % 8 === 0) await yieldFn(); + } + + return files; +} + +module.exports = { walkFolderAsync }; diff --git a/lib/folder-monitor.js b/lib/folder-monitor.js index 1c6914f..387a8d5 100644 --- a/lib/folder-monitor.js +++ b/lib/folder-monitor.js @@ -3,13 +3,15 @@ const path = require('path'); const chokidar = require('chokidar'); class FolderMonitor extends EventEmitter { - constructor() { + constructor({ watch = chokidar.watch } = {}) { super(); + this._watch = watch; this._watcher = null; this._settings = null; this._seenFiles = new Set(); this._batchBuffer = []; this._batchTimer = null; + this._initialScopes = new Set(); } get running() { @@ -23,9 +25,18 @@ class FolderMonitor extends EventEmitter { const folderPath = String(settings.folderPath || '').trim(); if (!folderPath) throw new Error('Kein Ordnerpfad angegeben'); + const scope = JSON.stringify([ + path.resolve(folderPath).toLowerCase(), + !!settings.recursive, + String(settings.filterMode || 'include'), + String(settings.extensions || '').trim().toLowerCase() + ]); + const includeInitial = !!settings.includeExisting && !this._initialScopes.has(scope); + if (includeInitial) this._initialScopes.add(scope); + const watchOptions = { persistent: true, - ignoreInitial: true, + ignoreInitial: !includeInitial, depth: settings.recursive ? undefined : 0, awaitWriteFinish: { stabilityThreshold: Math.max(1000, (settings.delaySec || 3) * 1000), @@ -33,7 +44,10 @@ class FolderMonitor extends EventEmitter { } }; - this._watcher = chokidar.watch(folderPath, watchOptions); + this._watcher = this._watch(folderPath, watchOptions); + if (includeInitial) { + this._watcher.once('ready', () => this.emit('initial-scan-complete')); + } this._watcher.on('add', (filePath) => this._onNewFile(filePath)); this._watcher.on('unlink', (filePath) => { // Allow re-added files (e.g. re-encoded) to be detected again @@ -41,6 +55,7 @@ class FolderMonitor extends EventEmitter { this._seenFiles.delete(normalized); }); this._watcher.on('error', (err) => this.emit('error', err)); + return { includesExisting: includeInitial }; } stop() { diff --git a/lib/log-mode.js b/lib/log-mode.js index 2a945ce..81f4e57 100644 --- a/lib/log-mode.js +++ b/lib/log-mode.js @@ -90,9 +90,7 @@ // Order matters: session first (longer, more specific) before daily. // Both regexes are anchored to $ with no nested/ambiguous quantifiers, so // matching is linear — the eslint security warning is precautionary. - // eslint-disable-next-line security/detect-unsafe-regex const sessionRe = /-session-\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(?:-\d+)?(\.[^.]+)?$/; - // eslint-disable-next-line security/detect-unsafe-regex const dailyRe = /-\d{4}-\d{2}-\d{2}(\.[^.]+)?$/; let out = fileName.replace(sessionRe, (m, ext) => ext || ''); out = out.replace(dailyRe, (m, ext) => ext || ''); diff --git a/lib/remote-server.js b/lib/remote-server.js index 486c5e3..6af9a1e 100644 --- a/lib/remote-server.js +++ b/lib/remote-server.js @@ -3,8 +3,8 @@ const crypto = require('crypto'); const { evaluateClientAllowed } = require('./ip-allowlist'); function timingSafeEqualStr(a, b) { - const x = Buffer.from(String(a == null ? '' : a)); - const y = Buffer.from(String(b == null ? '' : b)); + const x = Buffer.from(String(a === null || a === undefined ? '' : a)); + const y = Buffer.from(String(b === null || b === undefined ? '' : b)); return x.length === y.length && crypto.timingSafeEqual(x, y); } diff --git a/lib/secret-store.js b/lib/secret-store.js index 51412e9..0393431 100644 --- a/lib/secret-store.js +++ b/lib/secret-store.js @@ -10,6 +10,15 @@ const SENTINEL = 'enc:v1:'; const CRED_FIELDS = ['password', 'apiKey']; +class SecretStoreError extends Error { + constructor(code, message, cause) { + super(message); + this.name = 'SecretStoreError'; + this.code = code; + if (cause !== undefined) this.cause = cause; + } +} + let _safeStorageCache = undefined; function getSafeStorage() { if (_safeStorageCache !== undefined) return _safeStorageCache; @@ -29,16 +38,24 @@ function isEncrypted(value) { return typeof value === 'string' && value.startsWith(SENTINEL); } -function encryptField(value) { +function getAvailabilityStatus() { + return getSafeStorage() ? 'available' : 'unavailable'; +} + +function encryptField(value, options = {}) { if (!value || typeof value !== 'string') return value; if (isEncrypted(value)) return value; const ss = getSafeStorage(); - if (!ss) return value; + if (!ss) { + if (options.allowPlaintext === true) return value; + throw new SecretStoreError('SECRET_STORE_UNAVAILABLE', 'Sicherer Zugangsdaten-Speicher ist nicht verfügbar'); + } try { const buf = ss.encryptString(value); return SENTINEL + buf.toString('base64'); - } catch { - return value; + } catch (cause) { + if (options.allowPlaintext === true) return value; + throw new SecretStoreError('SECRET_STORE_ENCRYPT_FAILED', 'Zugangsdaten konnten nicht sicher verschlüsselt werden', cause); } } @@ -46,12 +63,14 @@ function decryptField(value) { if (!value || typeof value !== 'string') return value; if (!isEncrypted(value)) return value; const ss = getSafeStorage(); - if (!ss) return ''; + if (!ss) { + throw new SecretStoreError('SECRET_STORE_UNAVAILABLE', 'Sicherer Zugangsdaten-Speicher ist nicht verfügbar'); + } try { const buf = Buffer.from(value.slice(SENTINEL.length), 'base64'); return ss.decryptString(buf); - } catch { - return ''; + } catch (cause) { + throw new SecretStoreError('SECRET_STORE_DECRYPT_FAILED', 'Gespeicherte Zugangsdaten konnten nicht entschlüsselt werden', cause); } } @@ -69,7 +88,17 @@ function mapHosterAccounts(config, fn) { return config; } -function encryptCredentials(config) { return mapHosterAccounts(config, encryptField); } +function encryptCredentials(config, options = {}) { + return mapHosterAccounts(config, value => encryptField(value, options)); +} function decryptCredentials(config) { return mapHosterAccounts(config, decryptField); } -module.exports = { encryptField, decryptField, encryptCredentials, decryptCredentials, isEncrypted }; +module.exports = { + SecretStoreError, + getAvailabilityStatus, + encryptField, + decryptField, + encryptCredentials, + decryptCredentials, + isEncrypted +}; diff --git a/lib/stats.js b/lib/stats.js index 277c21e..fade21b 100644 --- a/lib/stats.js +++ b/lib/stats.js @@ -23,16 +23,18 @@ if (!file || !Array.isArray(file.results)) continue; for (const r of file.results) { if (!r || !r.hoster) continue; - const bucket = out[r.hoster] || (out[r.hoster] = { ok: 0, fail: 0, total: 0 }); + const bucket = out[r.hoster] || (out[r.hoster] = { ok: 0, fail: 0, skipped: 0, total: 0 }); bucket.total++; if (r.status === 'done') bucket.ok++; + else if (r.status === 'skipped') bucket.skipped++; else bucket.fail++; } } } for (const h of Object.keys(out)) { const b = out[h]; - b.rate = b.total > 0 ? b.ok / b.total : null; + const attempted = b.ok + b.fail; + b.rate = attempted > 0 ? b.ok / attempted : null; } return out; } @@ -61,7 +63,7 @@ for (const f of batchSummary.files) { if (!f || !Array.isArray(f.results)) continue; for (const r of f.results) { - if (!r || r.status === 'done') continue; + if (!r || r.status === 'done' || r.status === 'skipped') continue; const cat = classifyErrorCategory(r.error); buckets[cat].push({ fileName: f.name || f.fileName || '', @@ -74,6 +76,48 @@ return buckets; } + function mergeSkippedIntoSummary(summary, skippedJobs) { + const source = summary && typeof summary === 'object' ? summary : {}; + const merged = { + ...source, + files: Array.isArray(source.files) + ? source.files.map(file => ({ ...file, results: Array.isArray(file.results) ? [...file.results] : [] })) + : [] + }; + const existingJobIds = new Set(); + const filesByName = new Map(); + for (const file of merged.files) { + filesByName.set(String(file.name || file.fileName || ''), file); + for (const result of file.results) { + if (result?.jobId) existingJobIds.add(result.jobId); + } + } + let added = 0; + for (const skipped of Array.isArray(skippedJobs) ? skippedJobs : []) { + if (!skipped || (skipped.jobId && existingJobIds.has(skipped.jobId))) continue; + const fileName = String(skipped.fileName || skipped.file || '').split(/[\\/]/).pop() || ''; + let file = filesByName.get(fileName); + if (!file) { + file = { name: fileName, size: Number(skipped.size) || 0, results: [] }; + merged.files.push(file); + filesByName.set(fileName, file); + } + file.results.push({ + jobId: skipped.jobId || null, + hoster: skipped.hoster || '', + status: 'skipped', + error: skipped.reason || 'Übersprungen' + }); + if (skipped.jobId) existingJobIds.add(skipped.jobId); + added++; + } + merged.total = (Number(source.total) || 0) + added; + merged.succeeded = Number(source.succeeded) || 0; + merged.failed = Number(source.failed) || 0; + merged.skipped = (Number(source.skipped) || 0) + added; + return merged; + } + const RETRYABLE_CATEGORIES = new Set(['hoster-transient', 'network', 'unknown']); function isRetryableCategory(cat) { return RETRYABLE_CATEGORIES.has(cat); @@ -128,6 +172,7 @@ summarizePerHoster, classifyErrorCategory, summarizeBatchErrors, + mergeSkippedIntoSummary, isRetryableCategory, RETRYABLE_CATEGORIES, CATEGORY_LABELS, diff --git a/lib/support-bundle.js b/lib/support-bundle.js index 5ba0dc0..b246f7a 100644 --- a/lib/support-bundle.js +++ b/lib/support-bundle.js @@ -55,7 +55,7 @@ function redactLogText(text, secrets) { } function valueScrub(value, secrets) { - if (value == null) return value; + if (value === null || value === undefined) return value; const json = JSON.stringify(value); let scrubbed = json; if (Array.isArray(secrets)) { diff --git a/lib/updater.js b/lib/updater.js index fa24609..f540b4a 100644 --- a/lib/updater.js +++ b/lib/updater.js @@ -47,15 +47,13 @@ function resolveReleaseVersion(release) { return ''; } -function pickSetupAsset(assets) { +function pickSetupAsset(assets, remoteVersion = '') { if (!Array.isArray(assets)) return null; - // Prefer asset with "setup" in the name (case-insensitive) - const setup = assets.find(a => - /setup/i.test(a.name) && /\.exe$/i.test(a.name) - ); - if (setup) return setup; - // Fallback: any .exe - return assets.find(a => /\.exe$/i.test(a.name)) || null; + const candidates = assets.filter(asset => { + const name = String(asset?.name || ''); + return /setup/i.test(name) && /\.exe$/i.test(name) && (!remoteVersion || name.includes(remoteVersion)); + }); + return candidates.length === 1 ? candidates[0] : null; } function findLatestYml(assets) { @@ -137,7 +135,7 @@ async function checkForUpdate() { return cachedCheck; } - const setupAsset = pickSetupAsset(release.assets); + const setupAsset = pickSetupAsset(release.assets, remoteVersion); const latestYml = findLatestYml(release.assets); if (!setupAsset) { @@ -161,16 +159,37 @@ async function checkForUpdate() { return cachedCheck; } -async function parseLatestYml(url, fetchImpl = fetch) { - if (!url) return null; +async function parseLatestYml(url, expected = {}, fetchImpl = fetch) { + if (!url) throw new Error('Prüfsummen-Metadaten fehlen'); try { const res = await fetchImpl(url, { redirect: 'follow' }); + if (!res.ok) throw new Error(`Prüfsummen-Metadaten konnten nicht geladen werden: HTTP ${res.status}`); const text = await res.text(); - // Extract sha512 from latest.yml - const match = text.match(/sha512:\s*([A-Za-z0-9+/=]+)/); - return match ? match[1] : null; - } catch { - return null; + const version = text.match(/^version:\s*([^\r\n]+)$/m)?.[1]?.trim() || ''; + const assetPath = text.match(/^path:\s*([^\r\n]+)$/m)?.[1]?.trim() || ''; + const sha512 = text.match(/^sha512:\s*([A-Za-z0-9+/=]+)$/m)?.[1] || ''; + const sizeText = text.match(/^\s*size:\s*(\d+)\s*$/m)?.[1] || ''; + const size = Number(sizeText); + if (!version || !assetPath || !sha512 || !Number.isSafeInteger(size) || size <= 0) { + throw new Error('Prüfsummen-Metadaten sind unvollständig'); + } + const decodedSha = Buffer.from(sha512, 'base64'); + if (decodedSha.length !== 64 || decodedSha.toString('base64') !== sha512) { + throw new Error('Prüfsummen-Metadaten enthalten keine gültige SHA-512-Prüfsumme'); + } + if (expected.version && version !== expected.version) { + throw new Error('Prüfsummen-Metadaten gehören zu einer anderen Version'); + } + if (expected.assetName && path.basename(assetPath) !== path.basename(expected.assetName)) { + throw new Error('Prüfsummen-Metadaten gehören nicht zum ausgewählten Installer'); + } + if (expected.assetSize && size !== Number(expected.assetSize)) { + throw new Error('Prüfsummen-Metadaten enthalten eine abweichende Dateigröße'); + } + return { version, path: assetPath, size, sha512 }; + } catch (error) { + if (error && /Prüfsummen-Metadaten/.test(error.message)) throw error; + throw new Error(`Prüfsummen-Metadaten konnten nicht geladen werden: ${error.message}`); } } @@ -185,6 +204,7 @@ async function prepareUpdate(onProgress, options = {}) { activeAbort = new AbortController(); const signal = activeAbort.signal; const fetchImpl = options.fetchImpl || fetch; + let stagedInstallerPath = ''; try { // Stage: starting @@ -201,6 +221,15 @@ async function prepareUpdate(onProgress, options = {}) { if (!check.assetUrl || !check.assetName) { throw new Error('Update-Asset unvollständig (URL oder Name fehlt)'); } + if (!check.latestYmlUrl) { + throw new Error('Prüfsummen-Metadaten fehlen'); + } + const manifest = await parseLatestYml(check.latestYmlUrl, { + version: check.remoteVersion, + assetName: check.assetName, + assetSize: check.assetSize + }, fetchImpl); + const expectedSha = manifest.sha512; // Stage: downloading const tmpDir = options.tempDir || app.getPath('temp'); @@ -255,6 +284,9 @@ async function prepareUpdate(onProgress, options = {}) { } const fileBuffer = Buffer.concat(chunks); + if (fileBuffer.length !== manifest.size) { + throw new Error('Heruntergeladene Datei hat eine abweichende Größe'); + } // Stage: verifying if (onProgress) onProgress({ stage: 'verifying', percent: 0 }); @@ -263,21 +295,19 @@ async function prepareUpdate(onProgress, options = {}) { throw new Error('Heruntergeladene Datei ist keine gültige EXE'); } - // Optional SHA-512 verification from latest.yml - const expectedSha = await parseLatestYml(check.latestYmlUrl, fetchImpl); - if (expectedSha) { - const actualSha = crypto.createHash('sha512').update(fileBuffer).digest('base64'); - if (actualSha !== expectedSha) { - // Try hex comparison - const actualHex = crypto.createHash('sha512').update(fileBuffer).digest('hex'); - if (actualHex !== expectedSha.toLowerCase()) { - throw new Error('SHA-512 Prüfung fehlgeschlagen'); - } + const actualSha = crypto.createHash('sha512').update(fileBuffer).digest('base64'); + if (actualSha !== expectedSha) { + const actualHex = crypto.createHash('sha512').update(fileBuffer).digest('hex'); + if (actualHex !== expectedSha.toLowerCase()) { + throw new Error('SHA-512 Prüfung fehlgeschlagen'); } } - // Write to disk - fs.writeFileSync(installerPath, fileBuffer); + stagedInstallerPath = path.join(tmpDir, `${path.basename(check.assetName)}.${crypto.randomUUID()}.download`); + fs.writeFileSync(stagedInstallerPath, fileBuffer); + fs.rmSync(installerPath, { force: true }); + fs.renameSync(stagedInstallerPath, installerPath); + stagedInstallerPath = ''; const prepared = { installerPath, @@ -289,6 +319,7 @@ async function prepareUpdate(onProgress, options = {}) { return prepared; } catch (err) { + if (stagedInstallerPath) fs.rmSync(stagedInstallerPath, { force: true }); if (onProgress) onProgress({ stage: 'error', error: err.message }); throw err; } finally { @@ -322,4 +353,4 @@ function abortUpdate() { } } -module.exports = { checkForUpdate, fetchGithubReleaseNotes, prepareUpdate, launchPreparedUpdate, abortUpdate, isNewer, resolveReleaseVersion }; +module.exports = { checkForUpdate, fetchGithubReleaseNotes, prepareUpdate, launchPreparedUpdate, abortUpdate, isNewer, resolveReleaseVersion, pickSetupAsset, parseLatestYml }; diff --git a/lib/upload-confirmation.js b/lib/upload-confirmation.js new file mode 100644 index 0000000..1743b63 --- /dev/null +++ b/lib/upload-confirmation.js @@ -0,0 +1,39 @@ +const SUPPORTED_HOSTERS = new Set([ + 'doodstream.com', + 'voe.sx', + 'vidmoly.me', + 'byse.sx', + 'clouddrop.cc' +]); + +const FILE_CODE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{2,127}$/; + +function isExpectedHostUrl(value, expectedHost) { + if (typeof value !== 'string' || value.trim() === '') return false; + try { + const url = new URL(value); + const hostname = url.hostname.toLowerCase(); + return (url.protocol === 'http:' || url.protocol === 'https:') + && (hostname === expectedHost || hostname.endsWith(`.${expectedHost}`)); + } catch { + return false; + } +} + +function assertUploadConfirmation(result, hoster) { + const expectedHost = typeof hoster === 'string' ? hoster.trim().toLowerCase() : ''; + const fileCode = typeof result?.file_code === 'string' ? result.file_code.trim() : ''; + const urls = [result?.download_url, result?.embed_url].filter(value => ( + value !== null + && value !== undefined + && !(typeof value === 'string' && value.trim() === '') + )); + if (SUPPORTED_HOSTERS.has(expectedHost) + && FILE_CODE_PATTERN.test(fileCode) + && urls.every(value => isExpectedHostUrl(value, expectedHost))) { + return result; + } + throw new Error(`Upload zu ${hoster || 'unbekanntem Hoster'} wurde nicht bestätigt`); +} + +module.exports = { assertUploadConfirmation }; diff --git a/lib/upload-manager.js b/lib/upload-manager.js index bc751bb..da30586 100644 --- a/lib/upload-manager.js +++ b/lib/upload-manager.js @@ -1,5 +1,6 @@ const { EventEmitter } = require('events'); const path = require('path'); +const { assertUploadConfirmation } = require('./upload-confirmation'); const fs = require('fs'); const crypto = require('crypto'); const { uploadFile, prefetchBaseline } = require('./hosters'); @@ -410,13 +411,15 @@ class UploadManager extends EventEmitter { const files = Array.from(results.values()); const total = tasks.length; const succeeded = files.reduce((count, file) => count + file.results.filter((result) => result.status === 'done').length, 0); + const skipped = files.reduce((count, file) => count + file.results.filter((result) => result.status === 'skipped').length, 0); const summary = { id: batchId, timestamp: new Date().toISOString(), total, succeeded, - failed: total - succeeded, + failed: total - succeeded - skipped, + skipped, files }; @@ -488,19 +491,19 @@ class UploadManager extends EventEmitter { if (fileNotFound) { const error = 'Datei nicht gefunden'; emitFinalStatus('skipped', { error, attempt: 0 }); - recordFinalResult('error', { error }); + recordFinalResult('skipped', { error }); return; } if (fileSize <= 0) { const error = 'Datei ist leer (0 Bytes)'; emitFinalStatus('skipped', { error, attempt: 0 }); - recordFinalResult('error', { error }); + recordFinalResult('skipped', { error }); return; } if (settings.maxSizeMb > 0 && fileSize > settings.maxSizeMb * 1024 * 1024) { const error = `Datei zu groß (Max: ${settings.maxSizeMb} MB)`; emitFinalStatus('skipped', { error, attempt: 0 }); - recordFinalResult('error', { error }); + recordFinalResult('skipped', { error }); return; } @@ -576,7 +579,8 @@ class UploadManager extends EventEmitter { }); } - for (let attempt = 1; attempt <= maxAttempts && !memoSuspect; attempt++) { + const attemptsAllowed = memoSuspect ? 0 : maxAttempts; + for (let attempt = 1; attempt <= attemptsAllowed; attempt++) { if (signal.aborted || this.stopAfterActive) break; if (attempt > 1) { @@ -1189,6 +1193,11 @@ class UploadManager extends EventEmitter { } async _executeUpload(task, progressCb, signal, throttle, fileProbe) { + const result = await this._executeUploadUnchecked(task, progressCb, signal, throttle, fileProbe); + return assertUploadConfirmation(result, task.hoster); + } + + async _executeUploadUnchecked(task, progressCb, signal, throttle, fileProbe) { if (task.hoster === 'vidmoly.me' && task.username) { const vidmoly = new VidmolyUploader(); await vidmoly.login(task.username, task.password); diff --git a/lib/webhook-notify.js b/lib/webhook-notify.js index ffe6bc6..315b6b6 100644 --- a/lib/webhook-notify.js +++ b/lib/webhook-notify.js @@ -27,8 +27,9 @@ function summarizePerHosterFromBatch(summary) { if (!f || !Array.isArray(f.results)) continue; for (const r of f.results) { if (!r || !r.hoster) continue; - const b = out[r.hoster] || (out[r.hoster] = { ok: 0, fail: 0 }); + const b = out[r.hoster] || (out[r.hoster] = { ok: 0, fail: 0, skipped: 0 }); if (r.status === 'done') b.ok++; + else if (r.status === 'skipped') b.skipped++; else b.fail++; } } @@ -61,6 +62,7 @@ function buildWebhookRequest(url, summary, meta) { const total = Number(summary && summary.total) || 0; const succeeded = Number(summary && summary.succeeded) || 0; const failed = Number(summary && summary.failed) || 0; + const skipped = Number(summary && summary.skipped) || 0; const perHoster = summarizePerHosterFromBatch(summary); const duration = formatDurationShort(m.durationSec); @@ -78,8 +80,8 @@ function buildWebhookRequest(url, summary, meta) { const lines = [ `**Multi-Hoster-Upload — ${headline}**${m.machineName ? ` (${m.machineName})` : ''}`, language === 'de' - ? `✅ ${succeeded} ok · ❌ ${failed} Fehler · 📦 ${total} gesamt · ⏱ ${duration}` - : `✅ ${succeeded} succeeded · ❌ ${failed} failed · 📦 ${total} total · ⏱ ${duration}` + ? `✅ ${succeeded} ok · ❌ ${failed} Fehler${skipped > 0 ? ` · ⏭ ${skipped} übersprungen` : ''} · 📦 ${total} gesamt · ⏱ ${duration}` + : `✅ ${succeeded} succeeded · ❌ ${failed} failed${skipped > 0 ? ` · ⏭ ${skipped} skipped` : ''} · 📦 ${total} total · ⏱ ${duration}` ]; if (hosterLines) lines.push(hosterLines); const mention = resolveDiscordMention(m.mention); @@ -96,6 +98,7 @@ function buildWebhookRequest(url, summary, meta) { total, succeeded, failed, + skipped, durationSec: Math.round(Number(m.durationSec) || 0), aborted: !!m.aborted, perHoster, diff --git a/main.js b/main.js index 3fca2aa..bbea7bb 100644 --- a/main.js +++ b/main.js @@ -7,6 +7,7 @@ nativeTheme.themeSource = 'dark'; const path = require('path'); const fs = require('fs'); const ConfigStore = require('./lib/config-store'); +const secretStore = require('./lib/secret-store'); const UploadManager = require('./lib/upload-manager'); const { createSourceFileCleanup } = require('./lib/source-file-cleanup'); const SourceDeleteJournal = require('./lib/source-delete-journal'); @@ -23,6 +24,7 @@ const { createOnlineBackup, downloadOnlineBackup, uploadOnlineBackup } = require const { createPortableSettingsSnapshot, prepareImportedSettings } = require('./lib/settings-backup'); const { createSettingsImportGate } = require('./lib/settings-import-gate'); const FolderMonitor = require('./lib/folder-monitor'); +const { walkFolderAsync } = require('./lib/file-discovery'); const RemoteServer = require('./lib/remote-server'); const { maybeRotateLogFile } = require('./lib/log-rotation'); const { hosterLogToFileEnabled } = require('./lib/log-policy'); @@ -1697,6 +1699,8 @@ ipcMain.handle('get-config', () => { return configStore.load(); }); +ipcMain.handle('secret-store:status', () => ({ status: secretStore.getAvailabilityStatus() })); + ipcMain.handle('save-config', async (_event, config) => { assertConfigWriteAllowed(); await configStore.save(config); @@ -1942,7 +1946,7 @@ ipcMain.handle('select-folder', async () => { await rememberUploadBrowseDirectory(result.filePaths[0], true); const files = []; - for (const folder of result.filePaths) await walkFolderAsync(folder, files); + for (const folder of result.filePaths) files.push(...await walkFolderAsync(folder)); return files.length > 0 ? files.map(f => f.path) : null; }); @@ -1955,36 +1959,12 @@ ipcMain.handle('select-folder-with-sizes', async () => { await rememberUploadBrowseDirectory(result.filePaths[0], true); const files = []; - for (const folder of result.filePaths) await walkFolderAsync(folder, files); + for (const folder of result.filePaths) files.push(...await walkFolderAsync(folder)); return files.length > 0 ? files : null; }); -async function walkFolderAsync(rootDir, outFiles) { - const fsp = fs.promises; - const stack = [rootDir]; - let scanned = 0; - while (stack.length > 0) { - const dir = stack.pop(); - let entries; - try { entries = await fsp.readdir(dir, { withFileTypes: true }); } - catch { continue; } - for (const entry of entries) { - const full = path.join(dir, entry.name); - if (entry.isDirectory()) stack.push(full); - else if (entry.isFile()) { - let size = 0; - try { size = (await fsp.stat(full)).size; } catch {} - outFiles.push({ path: full, name: entry.name, size }); - } - } - if ((++scanned % 8) === 0) await new Promise(setImmediate); - } -} - ipcMain.handle('resolve-folder-files', async (_event, folderPath) => { - const files = []; - await walkFolderAsync(folderPath, files); - return files; + return walkFolderAsync(folderPath); }); ipcMain.handle('get-file-sizes', async (_event, paths) => { @@ -2024,7 +2004,12 @@ ipcMain.handle('start-upload', async (_event, payload) => { // Identify jobs that were skipped (no account/credentials) const taskJobIds = new Set(tasks.map(t => t.jobId).filter(Boolean)); const skippedJobs = jobs.filter(j => j.id && !taskJobIds.has(j.id)).map(j => ({ - jobId: j.id, hoster: j.hoster, reason: 'Kein gültiger Account für diesen Hoster' + jobId: j.id, + file: j.file, + fileName: j.fileName || path.basename(j.file || ''), + size: Number(j.bytesTotal) || 0, + hoster: j.hoster, + reason: 'Kein gültiger Account für diesen Hoster' })); if (skippedJobs.length > 0) { debugLog(` skipped ${skippedJobs.length} jobs: ${skippedJobs.map(s => s.hoster).join(', ')}`); @@ -2032,7 +2017,22 @@ ipcMain.handle('start-upload', async (_event, payload) => { debugLog(` tasks built: ${tasks.length}`); - if (tasks.length === 0) return { error: 'Keine gültigen Zugangsdaten für die gewählten Hoster.', skippedJobs }; + if (tasks.length === 0) { + const skippedSummary = stats.mergeSkippedIntoSummary({ + id: `skipped-${Date.now()}`, + timestamp: new Date().toISOString(), + total: 0, + succeeded: 0, + failed: 0, + skipped: 0, + files: [] + }, skippedJobs); + try { await configStore.appendHistory(skippedSummary); } catch (error) { + debugLog(`appendHistory for skipped jobs failed: ${error.message}`); + } + setImmediate(() => safeSend('upload-batch-done', skippedSummary)); + return { started: true, taskCount: 0, skippedJobs }; + } // Pre-resolve a fallback for every hoster that has one. Lets the upload // manager break out of the retry loop after a single generic failure and @@ -2210,6 +2210,7 @@ ipcMain.handle('start-upload', async (_event, payload) => { // create a fresh manager which the trailing `uploadManager = null` then // orphans (cancel/addJobs see null, the new batch keeps running invisibly). uploadManager.on('batch-done', async (summary) => { + summary = stats.mergeSkippedIntoSummary(summary, skippedJobs); debugLog(`batch-done: total=${summary.total} ok=${summary.succeeded} fail=${summary.failed}`); logMarker('BATCH END', { total: summary.total, ok: summary.succeeded, fail: summary.failed }); logMemorySnapshot('batch-done'); @@ -2625,7 +2626,11 @@ function readBackupFile(filePath) { } // --- Backup export / import --- -ipcMain.handle('export-backup', async () => { +ipcMain.handle('export-backup', async (_event, options = {}) => { + const password = typeof options?.password === 'string' ? options.password : undefined; + if (password !== undefined && (password.length < 8 || password.length > 1024)) { + throw new Error('Das Backup-Passwort muss zwischen 8 und 1024 Zeichen lang sein'); + } const _bd = new Date(); const _bdate = `${String(_bd.getDate()).padStart(2, '0')}-${String(_bd.getMonth() + 1).padStart(2, '0')}-${_bd.getFullYear()}`; const { canceled, filePath } = await dialog.showSaveDialog(mainWindow, { @@ -2640,9 +2645,10 @@ ipcMain.handle('export-backup', async () => { await waitForConfigStoreWrites(); const config = createPortableSettingsSnapshot(configStore.load()); if (filePath.toLowerCase().endsWith('.json')) { + if (typeof password === 'string') throw new Error('Passwortgeschützte Backups müssen als .mhu gespeichert werden'); fs.writeFileSync(filePath, JSON.stringify(config, null, 2), 'utf-8'); } else { - const encrypted = backupCrypto.encrypt(config); + const encrypted = backupCrypto.encrypt(config, password); fs.writeFileSync(filePath, encrypted); } return { ok: true, path: filePath }; @@ -2977,8 +2983,24 @@ function startFolderMonitor(settings) { folderMonitor.on('error', (err) => { debugLog(`folder-monitor error: ${err.message}`); }); - folderMonitor.start(settings); + folderMonitor.on('initial-scan-complete', async () => { + try { + const latest = configStore.load(); + const current = latest.globalSettings?.folderMonitor; + if (!current?.includeExisting || path.resolve(current.folderPath || '') !== path.resolve(settings.folderPath || '')) return; + await configStore.save({ + globalSettings: { + ...latest.globalSettings, + folderMonitor: { ...current, includeExisting: false } + } + }); + } catch (error) { + debugLog(`folder-monitor initial scan state failed: ${error.message}`); + } + }); + const result = folderMonitor.start(settings); debugLog(`folder-monitor started: ${settings.folderPath}`); + return result; } catch (err) { debugLog(`folder-monitor start failed: ${err.message}`); throw err; @@ -2986,8 +3008,8 @@ function startFolderMonitor(settings) { } ipcMain.handle('folder-monitor:start', (_event, settings) => { - startFolderMonitor(settings); - return { ok: true }; + const result = startFolderMonitor(settings); + return { ok: true, includesExisting: result?.includesExisting === true }; }); ipcMain.handle('folder-monitor:stop', () => { @@ -3180,11 +3202,11 @@ ipcMain.handle('diagnostics:save-settings', async (_e, incoming) => { enabled: !!(incoming && incoming.enabled), port: (incoming && Number(incoming.port)) || cur.port || 9110, bindMode: (incoming && incoming.bindMode === 'network') ? 'network' : 'local', - publicHost: (incoming && incoming.publicHost != null) ? String(incoming.publicHost).trim() : (cur.publicHost || ''), + publicHost: (incoming && incoming.publicHost !== null && incoming.publicHost !== undefined) ? String(incoming.publicHost).trim() : (cur.publicHost || ''), allowlist: (incoming && Array.isArray(incoming.allowlist)) ? incoming.allowlist.map((x) => String(x).trim()).filter(Boolean) : _diagAllowlist(cur), - label: (incoming && incoming.label != null) ? String(incoming.label) : cur.label + label: (incoming && incoming.label !== null && incoming.label !== undefined) ? String(incoming.label) : cur.label }; next.bindAddress = _diagBindHost(next); const gs = { ...cfg.globalSettings, diagnostics: next }; diff --git a/package-lock.json b/package-lock.json index f1715b9..96419de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,41 +1,33 @@ { "name": "multi-hoster-uploader", - "version": "2.1.11", + "version": "2.1.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "multi-hoster-uploader", - "version": "2.1.11", + "version": "2.1.12", "dependencies": { "chokidar": "^3.6.0", "undici": "^7.29.0", - "ws": "^8.21.0" + "ws": "^8.21.3" }, "devDependencies": { - "electron": "^41.3.0", - "electron-builder": "^26.8.1", - "eslint": "^10.1.0", - "eslint-plugin-security": "^4.0.0", + "electron": "^41.10.5", + "electron-builder": "^26.15.3", + "eslint": "^10.8.1", + "eslint-plugin-security": "^4.0.1", "rcedit": "^4.0.1" } }, - "node_modules/@develar/schema-utils": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "node_modules/@electron-internal/extract-zip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.5.tgz", + "integrity": "sha512-+bqFCP98pLI0Tt0XQo1TmlXtwjWchISndDOxCkEcIuUgXWpBnLyRI+2DU+mesvnMMX6L1XDqYNA0lXNDHd/yiA==", "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=22.12.0" } }, "node_modules/@electron/asar": { @@ -64,9 +56,9 @@ "license": "MIT" }, "node_modules/@electron/asar/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -118,49 +110,25 @@ "node": ">=10" } }, - "node_modules/@electron/fuses/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/fuses/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.1.0.tgz", + "integrity": "sha512-3kSBtG8ObcTVfXanm5vVJ6UnBLEVmVsRk1M+vGqCuMBV+XLCbJYuWQful+yIy0GQDsSlK0kHEriEHn7SPk4EnA==", "dev": true, "license": "MIT", "dependencies": { "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", + "env-paths": "^3.0.0", + "graceful-fs": "^4.2.11", "progress": "^2.0.3", - "semver": "^6.2.0", + "semver": "^7.6.3", "sumchecker": "^3.0.1" }, "engines": { - "node": ">=12" + "node": ">=22.12.0" }, "optionalDependencies": { - "global-agent": "^3.0.0" + "undici": "^7.24.4" } }, "node_modules/@electron/notarize": { @@ -194,29 +162,6 @@ "node": ">=10" } }, - "node_modules/@electron/notarize/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/notarize/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@electron/osx-sign": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", @@ -239,21 +184,6 @@ "node": ">=12.0.0" } }, - "node_modules/@electron/osx-sign/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", @@ -267,33 +197,10 @@ "url": "https://github.com/sponsors/gjtorikian/" } }, - "node_modules/@electron/osx-sign/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/osx-sign/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@electron/rebuild": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.0.4.tgz", - "integrity": "sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.2.0.tgz", + "integrity": "sha512-RKL/O+jGoXJMxrx/5771y1n0xTKmFuOYGO3gMmwypBM6rsH0kou0mswwdXA2JrhIkE4xyC7v9vGk0n6NPzgOxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -338,9 +245,9 @@ "license": "MIT" }, "node_modules/@electron/universal/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -348,9 +255,9 @@ } }, "node_modules/@electron/universal/node_modules/fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", "dev": true, "license": "MIT", "dependencies": { @@ -362,19 +269,6 @@ "node": ">=14.14" } }, - "node_modules/@electron/universal/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/@electron/universal/node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", @@ -391,16 +285,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@electron/universal/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@electron/windows-sign": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", @@ -424,9 +308,9 @@ } }, "node_modules/@electron/windows-sign/node_modules/fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", "dev": true, "license": "MIT", "optional": true, @@ -440,33 +324,6 @@ "node": ">=14.14" } }, - "node_modules/@electron/windows-sign/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/windows-sign/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -525,9 +382,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", - "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -561,9 +418,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", - "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -694,27 +551,69 @@ "node": ">=10" } }, - "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@malept/flatpak-bundler/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/@noble/hashes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.3.0.tgz", + "integrity": "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz", + "integrity": "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/webcrypto": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.7.1.tgz", + "integrity": "sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "tslib": "^2.8.1", + "webcrypto-core": "^1.9.2" + }, + "engines": { + "node": ">=14.18.0" } }, "node_modules/@sindresorhus/is": { @@ -831,18 +730,6 @@ "undici-types": "~7.16.0" } }, - "node_modules/@types/plist": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", - "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*", - "xmlbuilder": ">=11.0.1" - } - }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", @@ -853,25 +740,6 @@ "@types/node": "*" } }, - "node_modules/@types/verror": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", - "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@xmldom/xmldom": { "version": "0.8.13", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", @@ -882,13 +750,6 @@ "node": ">=10.0.0" } }, - "node_modules/7zip-bin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", - "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", - "dev": true, - "license": "MIT" - }, "node_modules/abbrev": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", @@ -949,16 +810,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -998,40 +849,36 @@ "node": ">= 8" } }, - "node_modules/app-builder-bin": { - "version": "5.0.0-alpha.12", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz", - "integrity": "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==", - "dev": true, - "license": "MIT" - }, "node_modules/app-builder-lib": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.8.1.tgz", - "integrity": "sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.15.3.tgz", + "integrity": "sha512-2VnyWkqsP5v5XbBhL3tD5Syx8iNPBYsoU7kY4S2fz7wg8Rj/nztWKCUzGKaFRTv0Xwf3/H058CR1Kvtd/3lRow==", "dev": true, "license": "MIT", "dependencies": { - "@develar/schema-utils": "~2.6.5", "@electron/asar": "3.4.1", "@electron/fuses": "^1.8.0", "@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", + "@peculiar/webcrypto": "^1.7.1", "@types/fs-extra": "9.0.13", + "ajv": "^8.18.0", + "asn1js": "^3.0.10", "async-exit-hook": "^2.0.1", - "builder-util": "26.8.1", - "builder-util-runtime": "9.5.1", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", "chromium-pickle-js": "^0.2.0", "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", - "electron-publish": "26.8.1", + "electron-publish": "26.15.3", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", @@ -1039,7 +886,8 @@ "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", - "minimatch": "^10.0.3", + "minimatch": "^10.2.5", + "pkijs": "^3.4.0", "plist": "3.1.0", "proper-lockfile": "^4.1.2", "resedit": "^1.7.0", @@ -1047,14 +895,15 @@ "tar": "^7.5.7", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0", + "unzipper": "^0.12.3", "which": "^5.0.0" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { - "dmg-builder": "26.8.1", - "electron-builder-squirrel-windows": "26.8.1" + "dmg-builder": "26.15.3", + "electron-builder-squirrel-windows": "26.15.3" } }, "node_modules/app-builder-lib/node_modules/@electron/get": { @@ -1104,6 +953,23 @@ "semver": "bin/semver.js" } }, + "node_modules/app-builder-lib/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/app-builder-lib/node_modules/ci-info": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", @@ -1120,42 +986,14 @@ "node": ">=8" } }, - "node_modules/app-builder-lib/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/app-builder-lib/node_modules/fs-extra/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/app-builder-lib/node_modules/fs-extra/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/app-builder-lib/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">=6" } }, "node_modules/app-builder-lib/node_modules/isexe": { @@ -1168,6 +1006,39 @@ "node": ">=18" } }, + "node_modules/app-builder-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/app-builder-lib/node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -1181,6 +1052,16 @@ "node": ">=10" } }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/app-builder-lib/node_modules/which": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", @@ -1204,26 +1085,19 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "node_modules/asn1js": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", "dev": true, - "license": "MIT", - "optional": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.5", + "tslib": "^2.8.1" + }, "engines": { - "node": ">=0.8" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" + "node": ">=12.0.0" } }, "node_modules/async": { @@ -1260,6 +1134,13 @@ "node": ">= 4.0.0" } }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, "node_modules/balanced-match": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", @@ -1303,6 +1184,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", @@ -1313,16 +1201,16 @@ "optional": true }, "node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/braces": { @@ -1337,42 +1225,6 @@ "node": ">=8" } }, - "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", - "optional": true, - "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", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -1381,16 +1233,14 @@ "license": "MIT" }, "node_modules/builder-util": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.8.1.tgz", - "integrity": "sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.15.3.tgz", + "integrity": "sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw==", "dev": true, "license": "MIT", "dependencies": { "@types/debug": "^4.1.6", - "7zip-bin": "~5.2.0", - "app-builder-bin": "5.0.0-alpha.12", - "builder-util-runtime": "9.5.1", + "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "cross-spawn": "^7.0.6", "debug": "^4.3.4", @@ -1403,12 +1253,15 @@ "stat-mode": "^1.0.0", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/builder-util-runtime": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz", - "integrity": "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz", + "integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==", "dev": true, "license": "MIT", "dependencies": { @@ -1419,42 +1272,14 @@ "node": ">=12.0.0" } }, - "node_modules/builder-util/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/builder-util/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/builder-util/node_modules/universalify": { + "node_modules/bytestreamjs": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">= 10.0.0" + "node": ">=6.0.0" } }, "node_modules/cacheable-lookup": { @@ -1574,24 +1399,6 @@ "node": ">=8" } }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1681,23 +1488,11 @@ "license": "MIT" }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "buffer": "^5.1.0" - } + "license": "MIT" }, "node_modules/cross-dirname": { "version": "0.1.0", @@ -1910,9 +1705,9 @@ "license": "MIT" }, "node_modules/dir-compare/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1934,85 +1729,16 @@ } }, "node_modules/dmg-builder": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.8.1.tgz", - "integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.15.3.tgz", + "integrity": "sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ==", "dev": true, "license": "MIT", "dependencies": { - "app-builder-lib": "26.8.1", - "builder-util": "26.8.1", + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", "fs-extra": "^10.1.0", - "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" - }, - "optionalDependencies": { - "dmg-license": "^1.0.11" - } - }, - "node_modules/dmg-builder/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dmg-builder/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/dmg-builder/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/dmg-license": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", - "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "@types/plist": "^3.0.1", - "@types/verror": "^1.10.3", - "ajv": "^6.10.0", - "crc": "^3.8.0", - "iconv-corefoundation": "^1.1.7", - "plist": "^3.0.4", - "smart-buffer": "^4.0.2", - "verror": "^1.10.0" - }, - "bin": { - "dmg-license": "bin/dmg-license.js" - }, - "engines": { - "node": ">=8" } }, "node_modules/dotenv": { @@ -2059,6 +1785,16 @@ "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==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -2076,37 +1812,37 @@ } }, "node_modules/electron": { - "version": "41.3.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-41.3.0.tgz", - "integrity": "sha512-2Q5aeocmFdeheZGDUTrAvSR3t+n0c3d104AJWWEnt7syJU0tE4VdibMYaPtQ47QuXSoUf0/xSsfUUvu/uSXIfg==", + "version": "41.10.5", + "resolved": "https://registry.npmjs.org/electron/-/electron-41.10.5.tgz", + "integrity": "sha512-grSJOhLddanWX1UfE8B4xA2kOOduUCfpYoOQ2D4zEYte8lSQtrd1HYbV4xErsZmm+V+1Q3yzCQLBsCxEIiw/QQ==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^24.9.0", - "extract-zip": "^2.0.1" + "@electron-internal/extract-zip": "^1.0.1", + "@electron/get": "^5.0.0", + "@types/node": "^24.9.0" }, "bin": { "electron": "cli.js" }, "engines": { - "node": ">= 12.20.55" + "node": ">= 22.12.0" } }, "node_modules/electron-builder": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.8.1.tgz", - "integrity": "sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.15.3.tgz", + "integrity": "sha512-a1KM5heqS3gQCZzizXEI8RjJy3QVogULPdeSknt76uLDpBIW/HDGsMg/XgP0riP6PI9COsRvFITKKGDqA8fJxA==", "dev": true, "license": "MIT", "dependencies": { - "app-builder-lib": "26.8.1", - "builder-util": "26.8.1", - "builder-util-runtime": "9.5.1", + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "ci-info": "^4.2.0", - "dmg-builder": "26.8.1", + "dmg-builder": "26.15.3", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", @@ -2121,66 +1857,29 @@ } }, "node_modules/electron-builder-squirrel-windows": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.8.1.tgz", - "integrity": "sha512-o288fIdgPLHA76eDrFADHPoo7VyGkDCYbLV1GzndaMSAVBoZrGvM9m2IehdcVMzdAZJ2eV9bgyissQXHv5tGzA==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.15.3.tgz", + "integrity": "sha512-Jc19XPV9y9+2bAdZPkXuVNGNIEFBq9poHC61l8Kv6FdK7DRG3+Ic0rerC0DXOaeHNz8yW0fg/JnF8GQROOF5MA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "app-builder-lib": "26.8.1", - "builder-util": "26.8.1", + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", "electron-winstaller": "5.4.0" } }, - "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", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/electron-builder/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-builder/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/electron-publish": { - "version": "26.8.1", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.8.1.tgz", - "integrity": "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w==", + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.15.3.tgz", + "integrity": "sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==", "dev": true, "license": "MIT", "dependencies": { "@types/fs-extra": "^9.0.11", - "builder-util": "26.8.1", - "builder-util-runtime": "9.5.1", + "aws4": "^1.13.2", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "form-data": "^4.0.5", "fs-extra": "^10.1.0", @@ -2188,44 +1887,6 @@ "mime": "^2.5.2" } }, - "node_modules/electron-publish/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/electron-publish/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-publish/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/electron-winstaller": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", @@ -2264,6 +1925,28 @@ "node": ">=6 <7 || >=8" } }, + "node_modules/electron-winstaller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-winstaller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2282,13 +1965,16 @@ } }, "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/err-code": { @@ -2319,9 +2005,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -2379,18 +2065,21 @@ } }, "node_modules/eslint": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.1.tgz", - "integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==", + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz", + "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", "dev": true, "license": "MIT", + "workspaces": [ + "packages/*" + ], "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.5.5", + "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.1", + "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2412,7 +2101,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", + "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -2435,9 +2124,9 @@ } }, "node_modules/eslint-plugin-security": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-4.0.0.tgz", - "integrity": "sha512-tfuQT8K/Li1ZxhFzyD8wPIKtlzZxqBcPr9q0jFMQ77wWAbKBVEhaMPVQRTMTvCMUDhwBe5vPVqQPwAGk/ASfxQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-4.0.1.tgz", + "integrity": "sha512-/lZCkOxPOWaf1jXAqgICrS8St3BMBccIPvhOSUYuV6VCr1o5nFVG998FnTLt6w2Nxb8Uo0nM8fzmnhp+GY/aEg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2495,6 +2184,22 @@ "node": ">=10.13.0" } }, + "node_modules/eslint/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/espree": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", @@ -2566,38 +2271,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "optional": true - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2619,15 +2292,22 @@ "dev": true, "license": "MIT" }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "node_modules/fast-uri": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, "node_modules/file-entry-cache": { "version": "8.0.0", @@ -2660,9 +2340,9 @@ "license": "MIT" }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -2733,35 +2413,35 @@ "license": "ISC" }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" } }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=12" } }, "node_modules/fs.realpath": { @@ -2902,9 +2582,9 @@ "license": "MIT" }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -2944,20 +2624,6 @@ "node": ">=10.0" } }, - "node_modules/global-agent/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -3076,9 +2742,9 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, "license": "MIT", "dependencies": { @@ -3150,59 +2816,6 @@ "node": ">= 14" } }, - "node_modules/iconv-corefoundation": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "cli-truncate": "^2.1.0", - "node-addon-api": "^1.6.3" - }, - "engines": { - "node": "^8.11.2 || >=10" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.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", - "optional": true - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3323,6 +2936,13 @@ "node": ">=8" } }, + "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" + }, "node_modules/isbinaryfile": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz", @@ -3372,10 +2992,20 @@ } }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -3427,11 +3057,14 @@ } }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "dev": true, "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -3661,9 +3294,9 @@ "license": "MIT" }, "node_modules/node-abi": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.28.0.tgz", - "integrity": "sha512-Qfp5XZL1cJDOabOT8H5gnqMTmM4NjvYzHp4I/Kt/Sl76OVkOBBHRFlPspGV0hYvMoqQsypFjT/Yp7Km0beXW9g==", + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.33.0.tgz", + "integrity": "sha512-vLBWCKb+7LWsX+TbfzWOkw0W81m377tyx3hOweBTjO43CXZnRGS1/JPWs20fr0PgZyDXk6ROYrylsEycK8raDA==", "dev": true, "license": "MIT", "dependencies": { @@ -3673,27 +3306,6 @@ "node": ">=22.12.0" } }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/node-api-version": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", @@ -3704,23 +3316,10 @@ "semver": "^7.3.5" } }, - "node_modules/node-api-version/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-gyp": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.3.0.tgz", - "integrity": "sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==", + "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": { @@ -3742,6 +3341,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/node-gyp/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/node-gyp/node_modules/isexe": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", @@ -3752,23 +3361,10 @@ "node": ">=20" } }, - "node_modules/node-gyp/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-gyp/node_modules/undici": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", - "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", "dev": true, "license": "MIT", "engines": { @@ -3791,6 +3387,13 @@ "node": "^20.17.0 || >=22.9.0" } }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/nopt": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", @@ -3955,13 +3558,6 @@ "url": "https://github.com/sponsors/jet2jet" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3981,6 +3577,37 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/pkijs/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -4046,6 +3673,13 @@ "node": "^20.17.0 || >=22.9.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" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -4083,9 +3717,9 @@ } }, "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", "dev": true, "license": "MIT", "dependencies": { @@ -4103,6 +3737,26 @@ "node": ">=6" } }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.2.0.tgz", + "integrity": "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -4142,6 +3796,22 @@ "read-binary-file-arch": "cli.js" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4174,6 +3844,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resedit": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", @@ -4256,6 +3936,13 @@ "node": ">=8.0" } }, + "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/safe-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", @@ -4266,13 +3953,6 @@ "regexp-tree": "~0.1.1" } }, - "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" - }, "node_modules/sanitize-filename": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", @@ -4284,9 +3964,9 @@ } }, "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -4294,13 +3974,16 @@ } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-compare": { @@ -4371,47 +4054,6 @@ "node": ">=10" } }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4451,6 +4093,16 @@ "node": ">= 6" } }, + "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/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -4506,9 +4158,9 @@ } }, "node_modules/tar": { - "version": "7.5.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", - "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -4558,44 +4210,6 @@ "fs-extra": "^10.0.0" } }, - "node_modules/temp-file/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/temp-file/node_modules/jsonfile": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", - "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/temp-file/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/tiny-async-pool": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", @@ -4617,9 +4231,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -4652,9 +4266,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -4665,9 +4279,9 @@ } }, "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, "license": "MIT", "engines": { @@ -4706,6 +4320,13 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4750,13 +4371,42 @@ "license": "MIT" }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">= 10.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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" } }, "node_modules/uri-js": { @@ -4776,20 +4426,25 @@ "dev": true, "license": "(WTFPL OR MIT)" }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webcrypto-core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.9.2.tgz", + "integrity": "sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" } }, "node_modules/which": { @@ -4844,9 +4499,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -4920,17 +4575,6 @@ "node": ">=12" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 270dd85..d38e8d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "multi-hoster-uploader", - "version": "2.1.11", + "version": "2.1.12", "description": "Upload files to doodstream, voe, vidmoly, byse simultaneously", "main": "main.js", "scripts": { @@ -8,6 +8,7 @@ "dev": "node scripts/dev-runner.cjs", "test": "node --test tests/*.test.js tests/ui-smoke.js", "test:backup-api": "npm --prefix services/backup-api test", + "verify": "npm run lint && npm test && npm run test:backup-api && npm audit --omit=dev", "lint": "eslint .", "dist": "electron-builder --win", "release:win": "electron-builder --publish never --win nsis portable" @@ -15,13 +16,13 @@ "dependencies": { "chokidar": "^3.6.0", "undici": "^7.29.0", - "ws": "^8.21.0" + "ws": "^8.21.3" }, "devDependencies": { - "electron": "^41.3.0", - "electron-builder": "^26.8.1", - "eslint": "^10.1.0", - "eslint-plugin-security": "^4.0.0", + "electron": "^41.10.5", + "electron-builder": "^26.15.3", + "eslint": "^10.8.1", + "eslint-plugin-security": "^4.0.1", "rcedit": "^4.0.1" }, "build": { diff --git a/preload.js b/preload.js index 5261de9..f4d1ca8 100644 --- a/preload.js +++ b/preload.js @@ -4,6 +4,7 @@ contextBridge.exposeInMainWorld('api', { // Config getConfig: () => ipcRenderer.invoke('get-config'), saveConfig: (config) => ipcRenderer.invoke('save-config', config), + getSecretStoreStatus: () => ipcRenderer.invoke('secret-store:status'), getHistory: () => ipcRenderer.invoke('get-history'), clearHistory: () => ipcRenderer.invoke('clear-history'), pruneHistory: (retention, opts) => ipcRenderer.invoke('prune-history', { retention, dryRun: !!(opts && opts.dryRun) }), @@ -75,7 +76,7 @@ contextBridge.exposeInMainWorld('api', { }, // Backup - exportBackup: () => ipcRenderer.invoke('export-backup'), + exportBackup: (options) => ipcRenderer.invoke('export-backup', options), importBackup: (legacyPassword) => ipcRenderer.invoke('import-backup', legacyPassword), createOnlineBackup: () => ipcRenderer.invoke('online-backup:create'), restoreOnlineBackup: (key) => ipcRenderer.invoke('online-backup:restore', key), diff --git a/renderer/app.js b/renderer/app.js index 9a10d33..c37df32 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -349,7 +349,7 @@ let _queueFilterCache = { filter: '', source: null, result: [] }; let historyRowsData = []; let historySortState = { key: 'date', direction: 'desc' }; let _historySortClicked = false; -let historySidebarCounts = { total: 0, success: 0, error: 0 }; +let historySidebarCounts = { total: 0, success: 0, error: 0, skipped: 0 }; let accountSidebarFilter = 'all'; let historySidebarFilter = 'all'; let _knownUpdateInfo = null; @@ -357,6 +357,8 @@ let _updateCheckBusy = false; let _updateInstallBusy = false; let _updateDialogReturnFocus = null; let _updateDialogInertState = []; +let _startupAutoResumeController = null; +let _startupAutoResumeCanceled = false; // Session-specific files for the "Files" panel (resets each session) let sessionFilesData = []; @@ -386,7 +388,12 @@ window.addEventListener('unhandledrejection', (e) => { // --- Init --- async function init() { - config = await window.api.getConfig(); + try { + config = await window.api.getConfig(); + } catch (error) { + await showAppAlert(error.message || String(error), 'Zugangsdaten gesperrt'); + throw error; + } setUiLanguage(config.globalSettings?.language); hosterSettings = config.hosterSettings || {}; autoHealthCheckEnabled = loadAutoCheckPreference(); @@ -553,6 +560,7 @@ async function init() { } catch {} scheduleStartupAccountCheck(); + scheduleRestoredQueueAutoStart(); } // --- Tab switching --- @@ -659,7 +667,7 @@ function initMenuBar() { void panel.offsetHeight; panel.classList.add('menu-closing'); const finish = () => { - if (panelTokens.get(panel) !== token) return; + if (!Object.is(panelTokens.get(panel), token)) return; panel.style.display = 'none'; panel.classList.remove('menu-closing'); }; @@ -1597,6 +1605,54 @@ function buildQueuePreview() { persistQueueStateSoon(); } +function hideStartupResumeBanner() { + const banner = document.getElementById('startupResumeBanner'); + if (banner) banner.hidden = true; +} + +function cancelStartupQueueAutoStart() { + _startupAutoResumeCanceled = true; + _startupAutoResumeController?.cancel(); + hideStartupResumeBanner(); +} + +async function startRestoredQueueAfterChecks(jobIds) { + const message = document.getElementById('startupResumeMessage'); + async function waitForAccountCheck() { + if (!healthCheckRunning || _startupAutoResumeCanceled) return; + if (message) message.textContent = localizeUiText('Warte auf Account-Prüfung…'); + await new Promise(resolve => setTimeout(resolve, 250)); + return waitForAccountCheck(); + } + await waitForAccountCheck(); + hideStartupResumeBanner(); + if (_startupAutoResumeCanceled || uploading || config?.globalSettings?.autoStartRestoredQueue !== true) return; + const jobs = window.AutoResume.getAutoResumeJobs(queueJobs, jobIds); + if (!jobs.length) return; + await startSelectedUpload(jobs); +} + +function scheduleRestoredQueueAutoStart() { + if (config?.globalSettings?.autoStartRestoredQueue !== true || !window.AutoResume) return; + const jobs = window.AutoResume.getAutoResumeJobs(queueJobs); + if (!jobs.length) return; + _startupAutoResumeCanceled = false; + const banner = document.getElementById('startupResumeBanner'); + const message = document.getElementById('startupResumeMessage'); + const cancelButton = document.getElementById('cancelStartupResumeBtn'); + _startupAutoResumeController = window.AutoResume.createAutoResumeController({ + delaySeconds: 8, + onTick: (seconds, count) => { + if (banner) banner.hidden = false; + if (message) message.textContent = localizeUiText(`Wiederhergestellte Warteschlange startet in ${seconds} s (${count} Jobs).`); + }, + onStart: jobIds => { startRestoredQueueAfterChecks(jobIds); }, + onCancel: hideStartupResumeBanner + }); + if (cancelButton) cancelButton.onclick = cancelStartupQueueAutoStart; + _startupAutoResumeController.schedule(jobs.map(job => job.id)); +} + // --- Job Index Management --- function rebuildJobIndex() { _jobIndexById.clear(); @@ -2332,14 +2388,48 @@ function copySelectedRecentLinks() { // --- Backup export / import --- async function doBackupExport() { try { + const protectInput = document.getElementById('protectLocalBackupInput'); + let password; + if (protectInput?.checked) { + const passwordInput = document.getElementById('localBackupPasswordInput'); + const confirmInput = document.getElementById('localBackupPasswordConfirmInput'); + password = passwordInput?.value || ''; + if (password.length < 8) { + await showAppAlert('Das Backup-Passwort muss mindestens 8 Zeichen lang sein.', 'Passwort prüfen'); + passwordInput?.focus(); + return; + } + if (!secretsMatch(password, confirmInput?.value || '')) { + await showAppAlert('Die beiden Backup-Passwörter stimmen nicht überein.', 'Passwort prüfen'); + confirmInput?.focus(); + return; + } + } await flushPendingSettingsSaves(); - const result = await window.api.exportBackup(); - if (result && result.ok) showCopyToast('Backup exportiert'); + const result = await window.api.exportBackup(password === undefined ? {} : { password }); + if (result && result.ok) { + const passwordInput = document.getElementById('localBackupPasswordInput'); + const confirmInput = document.getElementById('localBackupPasswordConfirmInput'); + if (passwordInput) passwordInput.value = ''; + if (confirmInput) confirmInput.value = ''; + showCopyToast('Backup exportiert'); + } } catch (err) { await showAppAlert('Export fehlgeschlagen: ' + (err.message || err), 'Export fehlgeschlagen'); } } +function secretsMatch(left, right) { + const a = String(left || ''); + const b = String(right || ''); + const length = Math.max(a.length, b.length); + let difference = a.length ^ b.length; + for (let index = 0; index < length; index++) { + difference |= (a.charCodeAt(index) || 0) ^ (b.charCodeAt(index) || 0); + } + return difference === 0; +} + function applyImportedConfig(importedConfig, message) { const pendingQueue = buildPersistedQueueState(); config = { @@ -2745,6 +2835,7 @@ function serializeUploadJob(job) { async function startUpload(opts) { if (uploading) return; + if (!(opts && opts._restoredAutoStart)) cancelStartupQueueAutoStart(); if (!(opts && opts._autoRetry)) _cancelAutoRetry(true); else _cancelAutoRetry(false); uploading = true; // set immediately to prevent double-click race @@ -2814,7 +2905,7 @@ function _markSkippedJobs(result) { for (const skipped of result.skippedJobs) { const job = _jobIndexById.get(skipped.jobId); if (job) { - job.status = 'error'; + job.status = 'skipped'; job.error = skipped.reason || 'Kein gültiger Account'; } } @@ -3240,7 +3331,7 @@ function _retryFailedFromBuckets(buckets, transientOnly) { if (toRetry.length === 0) return; const jobsToRetry = []; for (const item of toRetry) { - const job = queueJobs.find(j => (j.fileName === item.fileName) && (j.hoster === item.hoster) && (j.status === 'error' || j.status === 'skipped')); + const job = queueJobs.find(j => (j.fileName === item.fileName) && (j.hoster === item.hoster) && j.status === 'error'); if (job) { job.status = 'queued'; job.progress = 0; @@ -3565,6 +3656,9 @@ function applySummaryResults(summary) { } else if (result.status === 'error') { job.status = 'error'; job.error = result.error || 'Fehlgeschlagen'; + } else if (result.status === 'skipped') { + job.status = 'skipped'; + job.error = result.error || 'Übersprungen'; } maybeAddSessionFile(job); } @@ -3580,38 +3674,7 @@ function applySummaryResults(summary) { let _queueStatsCache = null; function _computeQueueStats() { if (_queueStatsCache) return _queueStatsCache; - - let remaining = 0, inProgress = 0, done = 0, errors = 0; - let bytesRemaining = 0, totalSize = 0, remainingSize = 0, inProgressBytes = 0; - const total = queueJobs.length; - - for (let i = 0; i < total; i++) { - const job = queueJobs[i]; - const s = job.status; - const bt = job.bytesTotal || 0; - const bu = job.bytesUploaded || 0; - totalSize += bt; - - if (s === 'uploading' || s === 'getting-server' || s === 'retrying') { - inProgress++; - remaining++; - inProgressBytes += bu; - bytesRemaining += Math.max(0, bt - bu); - remainingSize += Math.max(0, bt - bu); - } else if (s === 'preview' || s === 'queued') { - remaining++; - bytesRemaining += Math.max(0, bt - bu); - remainingSize += Math.max(0, bt - bu); - } else if (s === 'done') { - done++; - } else if (s === 'error') { - errors++; - } else if (s !== 'skipped') { - remainingSize += Math.max(0, bt - bu); - } - } - - _queueStatsCache = { total, remaining, inProgress, done, errors, bytesRemaining, totalSize, remainingSize, inProgressBytes }; + _queueStatsCache = window.QueueStats.calculateQueueStats(queueJobs); (typeof queueMicrotask === 'function' ? queueMicrotask : (fn) => Promise.resolve().then(fn))(() => { _queueStatsCache = null; }); return _queueStatsCache; } @@ -3765,7 +3828,7 @@ function setAccountSidebarFilter(value) { } function setHistorySidebarFilter(value) { - if (!['all', 'success', 'error'].includes(value)) return; + if (!['all', 'success', 'error', 'skipped'].includes(value)) return; historySidebarFilter = value; _syncSidebarFilterButtons('[data-history-filter]', 'historyFilter', value); const container = document.getElementById('historyContainer'); @@ -3779,6 +3842,7 @@ function updateHistorySidebarSummary() { _setSidebarCount('historySidebarAllCount', historySidebarCounts.total); _setSidebarCount('historySidebarSuccessCount', historySidebarCounts.success); _setSidebarCount('historySidebarErrorCount', historySidebarCounts.error); + _setSidebarCount('historySidebarSkippedCount', historySidebarCounts.skipped); const retention = document.getElementById('historySidebarRetention'); const select = document.getElementById('historyRetentionSelect'); const labels = { @@ -4191,6 +4255,14 @@ function renderSettings() { +
Noch keine Uploads.
'; @@ -6196,7 +6322,7 @@ async function loadHistory() { historySortState = { key: 'date', direction: 'desc' }; historyRowsData = []; - historySidebarCounts = { total: 0, success: 0, error: 0 }; + historySidebarCounts = { total: 0, success: 0, error: 0, skipped: 0 }; let order = 0; for (const batch of history) { @@ -6204,17 +6330,15 @@ async function loadHistory() { for (const file of (batch.files || [])) { for (const result of (file.results || [])) { historySidebarCounts.total++; - const isError = result.status === 'aborted' || result.status === 'error'; - if (isError) historySidebarCounts.error++; - else historySidebarCounts.success++; - const detail = isError - ? String(result.error || result.message || (result.status === 'aborted' ? 'Abgebrochen' : 'Fehlgeschlagen')) - : (result.download_url || result.embed_url || ''); + const category = window.HistoryStatus.classifyHistoryStatus(result.status); + historySidebarCounts[category]++; + const detail = window.HistoryStatus.historyDetail(result); historyRowsData.push({ date: dt.text, dateTs: dt.ts, rawTimestamp: batch.timestamp, filename: file.name || '', host: result.hoster || '', link: detail, - isError, order: order++ + status: result.status || '', category, + isError: category === 'error', order: order++ }); } } @@ -6331,7 +6455,7 @@ function _renderRecentVirtualRows() { tbody.innerHTML = parts.join(''); } -function renderRecentUploadsPanel(appendOnly = false) { +function renderRecentUploadsPanel(_appendOnly = false) { const tbody = document.getElementById('recentFilesBody'); if (!tbody) return; _recentPendingAppends = 0; @@ -6468,7 +6592,7 @@ function _renderHistoryVirtualRows() { const row = _historyWorking[i]; const link = row.link || ''; parts.push('