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() { +
+
+ + Nur verwenden, wenn der sichere Betriebssystem-Speicher dauerhaft nicht verfügbar ist. Passwörter und API-Keys liegen dann lesbar in der Konfigurationsdatei. + Prüfe… +
+ +
Programmupdate
@@ -4236,6 +4308,13 @@ function renderSettings() {
+
+
+ + Startet wartende Uploads nach einem sichtbaren, abbrechbaren Countdown. Fehler und übersprungene Einträge bleiben unangetastet. +
+ +
Quelldateien
@@ -4293,6 +4372,10 @@ function renderSettings() {
+
+ + +
@@ -4472,6 +4555,17 @@ function renderSettings() {
+
+
+ + Ohne dieses Passwort kann das Backup nicht importiert werden. +
+ +
+
@@ -4752,6 +4846,12 @@ function renderSettings() { document.getElementById('exportBackupBtn').addEventListener('click', () => doBackupExport()); document.getElementById('importBackupBtn').addEventListener('click', () => doBackupImport()); + document.getElementById('protectLocalBackupInput').addEventListener('change', (event) => { + const fields = document.getElementById('localBackupPasswordFields'); + if (!fields) return; + fields.hidden = !event.target.checked; + if (event.target.checked) document.getElementById('localBackupPasswordInput')?.focus(); + }); document.getElementById('createOnlineBackupBtn').addEventListener('click', () => doOnlineBackupCreate()); document.getElementById('copyOnlineBackupKeyBtn').addEventListener('click', async () => { const key = document.getElementById('onlineBackupKeyOutput').value; @@ -4770,6 +4870,13 @@ function renderSettings() { document.getElementById('chooseLogFilePathBtn')?.addEventListener('click', chooseLogFilePath); document.getElementById('openLogFolderBtn')?.addEventListener('click', () => window.api.openLogFolder()); document.getElementById('manualUpdateCheckBtn')?.addEventListener('click', requestUpdateCheck); + window.api.getSecretStoreStatus().then(result => { + const badge = document.getElementById('secretStoreStatus'); + if (!badge) return; + const available = result?.status === 'available'; + badge.textContent = available ? 'Sicher verfügbar' : 'Nicht verfügbar'; + badge.classList.toggle('active', available); + }).catch(() => {}); _syncHeaderUpdateState(); container.querySelectorAll('.settings-autosave').forEach((input) => { const eventName = input.type === 'checkbox' || input.tagName === 'SELECT' ? 'change' : 'input'; @@ -4787,6 +4894,15 @@ function renderSettings() { }); if (!confirmed) input.checked = false; } + if (input.id === 'allowPlaintextCredentialStorageInput' && input.checked) { + const confirmed = await showAppConfirm({ + title: 'Zugangsdaten unverschlüsselt speichern?', + message: 'Passwörter und API-Keys werden dann im Klartext auf diesem PC gespeichert. Andere Benutzer oder Programme mit Dateizugriff können sie lesen.', + confirmText: 'Klartext-Speicherung erlauben', + danger: true + }); + if (!confirmed) input.checked = false; + } markSettingsDirty(); }); }); @@ -4911,6 +5027,8 @@ async function performSaveSettings(options = {}) { return (v === 'single' || v === 'daily' || v === 'session') ? v : 'single'; })(), resumeQueueOnLaunch: elChk('resumeQueueOnLaunchInput', cur.resumeQueueOnLaunch !== false), + autoStartRestoredQueue: elChk('autoStartRestoredQueueInput', !!cur.autoStartRestoredQueue), + allowPlaintextCredentialStorage: elChk('allowPlaintextCredentialStorageInput', !!cur.allowPlaintextCredentialStorage), parallelUploadCount: elInt('parallelUploadCountInput', cur.parallelUploadCount ?? 0, 0, 0, 100), scaleParallelUploads: elChk('scaleParallelUploadsInput', !!cur.scaleParallelUploads), removeFromQueueOnDone: elChk('removeFromQueueOnDoneInput', !!cur.removeFromQueueOnDone), @@ -4931,6 +5049,7 @@ async function performSaveSettings(options = {}) { enabled: elChk('fmEnabledInput', !!curFm.enabled), folderPath: elTxt('fmFolderPathInput', curFm.folderPath || '').trim(), recursive: elChk('fmRecursiveInput', !!curFm.recursive), + includeExisting: elChk('fmIncludeExistingInput', !!curFm.includeExisting), filterMode: (() => { const el = document.getElementById('fmFilterModeInput'); return el ? (el.value || 'include') : (curFm.filterMode || 'include'); })(), extensions: elTxt('fmExtensionsInput', curFm.extensions || '').trim(), skipDuplicates: elChk('fmSkipDuplicatesInput', curFm.skipDuplicates !== false), @@ -5003,7 +5122,14 @@ async function performSaveSettings(options = {}) { const badge = document.getElementById('folderMonitorStatusBadge'); if (fmSettings && fmSettings.enabled && fmSettings.folderPath) { try { - await window.api.folderMonitorStart(fmSettings); + const folderStart = await window.api.folderMonitorStart(fmSettings); + if (folderStart?.includesExisting) { + fmSettings.includeExisting = false; + globalSettings.folderMonitor.includeExisting = false; + config.globalSettings.folderMonitor.includeExisting = false; + const includeExistingInput = document.getElementById('fmIncludeExistingInput'); + if (includeExistingInput) includeExistingInput.checked = false; + } if (badge) { badge.textContent = 'Aktiv'; badge.className = 'panel-status active'; } } catch { if (badge) { badge.textContent = 'Fehler'; badge.className = 'panel-status'; } @@ -6169,7 +6295,7 @@ async function loadHistory() { history = await window.api.getHistory(); } catch (error) { historyRowsData = []; - historySidebarCounts = { total: 0, success: 0, error: 0 }; + historySidebarCounts = { total: 0, success: 0, error: 0, skipped: 0 }; updateHistorySidebarSummary(); syncHistoryClearAction(); if (container) container.innerHTML = ``; @@ -6187,7 +6313,7 @@ async function loadHistory() { } if (!history || history.length === 0) { historyRowsData = []; - historySidebarCounts = { total: 0, success: 0, error: 0 }; + historySidebarCounts = { total: 0, success: 0, error: 0, skipped: 0 }; updateHistorySidebarSummary(); syncHistoryClearAction(); container.innerHTML = '

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('`); @@ -6481,15 +6605,16 @@ function _renderHistoryVirtualRows() { parts.push(escapeAttr(link)); parts.push('">'); parts.push(escapeHtml(link)); - parts.push('
'); + parts.push(''); + if (row.category === 'success' && link) parts.push(''); + parts.push('
'); } if (bottomPad > 0) parts.push(``); tbody.innerHTML = parts.join(''); } function _getVisibleHistoryRows() { - if (historySidebarFilter === 'success') return historyRowsData.filter(row => !row.isError); - if (historySidebarFilter === 'error') return historyRowsData.filter(row => row.isError); + if (historySidebarFilter !== 'all') return historyRowsData.filter(row => row.category === historySidebarFilter); return historyRowsData; } @@ -6527,7 +6652,7 @@ function renderHistoryTable(container) { }; container.innerHTML = ` - ${headerCell('date', 'Datum')}${headerCell('filename', 'Dateiname')}${headerCell('host', 'Hoster')}${headerCell('link', 'Link')} + ${headerCell('date', 'Datum')}${headerCell('filename', 'Dateiname')}${headerCell('host', 'Hoster')}${headerCell('link', 'Ergebnis')}
`; if (!_historyListenersBound) { @@ -6558,7 +6683,7 @@ function renderHistoryTable(container) { return; } const row = e.target.closest('.history-row'); - if (row && !row.classList.contains('error')) { + if (row && row.classList.contains('success')) { const link = row.dataset.link; if (link) { window.api.copyToClipboard(link); showCopyToast('Link kopiert'); } } @@ -7666,7 +7791,7 @@ function formatDuration(seconds) { function updateStatsPanel() { const stats = _computeQueueStats(); - const remaining = stats.total - stats.done - stats.errors; + const remaining = stats.remaining; const el = (id) => document.getElementById(id); if (el('statQueueTotal')) el('statQueueTotal').textContent = stats.total; diff --git a/renderer/auto-resume.js b/renderer/auto-resume.js new file mode 100644 index 0000000..3a37ef7 --- /dev/null +++ b/renderer/auto-resume.js @@ -0,0 +1,66 @@ +(function (root, factory) { + const api = factory(); + if (typeof module === 'object' && module.exports) module.exports = api; + if (root) root.AutoResume = api; +})(typeof window !== 'undefined' ? window : globalThis, function () { + function getAutoResumeJobs(jobs, jobIds) { + const allowedJobIds = Array.isArray(jobIds) ? new Set(jobIds) : null; + return Array.isArray(jobs) + ? jobs.filter(job => job && (job.status === 'queued' || job.status === 'preview') && job.file && job.hoster && (!allowedJobIds || allowedJobIds.has(job.id))) + : []; + } + + function createAutoResumeController(options = {}) { + const delaySeconds = Math.max(1, Number(options.delaySeconds) || 8); + const setIntervalFn = options.setIntervalFn || setInterval; + const clearIntervalFn = options.clearIntervalFn || clearInterval; + const onTick = options.onTick || (() => {}); + const onStart = options.onStart || (() => {}); + const onCancel = options.onCancel || (() => {}); + let timer = null; + let pending = false; + + function stopTimer() { + if (timer === null) return; + clearIntervalFn(timer); + timer = null; + } + + function schedule(jobIds) { + if (pending || !Array.isArray(jobIds)) return false; + const plannedJobIds = [...new Set(jobIds.filter(jobId => typeof jobId === 'string' && jobId))]; + if (!plannedJobIds.length) return false; + pending = true; + let remaining = delaySeconds; + onTick(remaining, plannedJobIds.length); + timer = setIntervalFn(() => { + if (!pending) return; + remaining--; + if (remaining > 0) { + onTick(remaining, plannedJobIds.length); + return; + } + pending = false; + stopTimer(); + onStart(plannedJobIds); + }, 1000); + return true; + } + + function cancel() { + if (!pending) return false; + pending = false; + stopTimer(); + onCancel(); + return true; + } + + return { + schedule, + cancel, + get pending() { return pending; } + }; + } + + return { getAutoResumeJobs, createAutoResumeController }; +}); diff --git a/renderer/history-status.js b/renderer/history-status.js new file mode 100644 index 0000000..581512e --- /dev/null +++ b/renderer/history-status.js @@ -0,0 +1,22 @@ +(function (root, factory) { + const api = factory(); + if (typeof module === 'object' && module.exports) module.exports = api; + if (root) root.HistoryStatus = api; +})(typeof globalThis !== 'undefined' ? globalThis : this, function () { + function classifyHistoryStatus(status) { + if (status === 'done') return 'success'; + if (status === 'skipped') return 'skipped'; + return 'error'; + } + + function historyDetail(result) { + const category = classifyHistoryStatus(result?.status); + if (category === 'success') return String(result?.download_url || result?.embed_url || ''); + if (result?.error || result?.message) return String(result.error || result.message); + if (result?.status === 'aborted') return 'Abgebrochen'; + if (category === 'skipped') return 'Übersprungen'; + return 'Fehlgeschlagen'; + } + + return { classifyHistoryStatus, historyDetail }; +}); diff --git a/renderer/i18n.js b/renderer/i18n.js index cceddcc..66f4a4e 100644 --- a/renderer/i18n.js +++ b/renderer/i18n.js @@ -188,6 +188,9 @@ ['Nach Abschluss aus der Liste entfernen', 'Remove from the list after completion'], ['Erfolgreich hochgeladene Dateien verschwinden automatisch aus der Upload-Liste.', 'Successfully uploaded files are automatically removed from the upload list.'], ['Warteschlange beim Start wiederherstellen', 'Restore queue on startup'], + ['Wiederhergestellte Warteschlange automatisch starten', 'Start restored queue automatically'], + ['Startet wartende Uploads nach einem sichtbaren, abbrechbaren Countdown. Fehler und übersprungene Einträge bleiben unangetastet.', 'Starts waiting uploads after a visible, cancelable countdown. Failed and skipped entries remain untouched.'], + ['Warte auf Account-Prüfung…', 'Waiting for account check…'], ['Noch nicht abgeschlossene Uploads werden beim nächsten Programmstart erneut angezeigt.', 'Incomplete uploads are shown again the next time the application starts.'], ['Quelldateien', 'Source files'], ['Quelldatei nach vollständigem Upload dauerhaft löschen', 'Permanently delete source file after complete upload'], @@ -223,6 +226,7 @@ ['Verhalten', 'Behavior'], ['Aktiviert', 'Enabled'], ['Unterordner einbeziehen', 'Include subfolders'], + ['Vorhandene Dateien einmalig einlesen', 'Import existing files once'], ['Duplikate überspringen', 'Skip duplicates'], ['Auto-Upload starten', 'Start uploads automatically'], ['Hoster-Vorauswahl', 'Host preselection'], @@ -277,10 +281,18 @@ ['Online importieren', 'Import online'], ['Behandle den Schlüssel wie ein Passwort. Wer ihn besitzt, kann die verschlüsselten Einstellungen entschlüsseln.', 'Treat the key like a password. Anyone who has it can decrypt the encrypted settings.'], ['Lokales Datei-Backup', 'Local file backup'], + ['Mit eigenem Passwort schützen', 'Protect with your own password'], + ['Ohne dieses Passwort kann das Backup nicht importiert werden.', 'The backup cannot be imported without this password.'], + ['Passwort (mindestens 8 Zeichen)', 'Password (at least 8 characters)'], + ['Passwort wiederholen', 'Repeat password'], + ['Das Backup-Passwort muss mindestens 8 Zeichen lang sein.', 'The backup password must be at least 8 characters long.'], + ['Die beiden Backup-Passwörter stimmen nicht überein.', 'The two backup passwords do not match.'], + ['Passwort prüfen', 'Check password'], ['Datei exportieren', 'Export file'], ['Datei importieren', 'Import file'], ['Alle Uploads', 'All uploads'], ['Erfolgreich', 'Successful'], + ['Ergebnis', 'Result'], ['Aktive Regel', 'Active rule'], ['Alles behalten', 'Keep everything'], ['Links wiederfinden, kopieren oder als Datei exportieren.', 'Find links, copy them, or export them as a file.'], @@ -311,6 +323,7 @@ ['Wartende Uploads anzeigen', 'Show queued uploads'], ['Fertige Uploads anzeigen', 'Show completed uploads'], ['Fehlgeschlagene Uploads anzeigen', 'Show failed uploads'], + ['Übersprungene Uploads anzeigen', 'Show skipped uploads'], ['Alle Uploads starten', 'Start all uploads'], ['Ausgewählte Uploads starten', 'Start selected uploads'], ['Ausgewählte Datei erneut hochladen', 'Upload selected file again'], @@ -454,6 +467,20 @@ ['Keine gültige .mhu Backup-Datei', 'Not a valid .mhu backup file'], ['Falsches Passwort oder beschädigte Datei', 'Incorrect password or damaged file'], ['Dieses Backup wurde mit einem Passwort verschlüsselt', 'This backup was encrypted with a password'], + ['Das Backup-Passwort darf nicht leer sein', 'The backup password must not be empty'], + ['Das Backup-Passwort muss zwischen 8 und 1024 Zeichen lang sein', 'The backup password must be between 8 and 1024 characters long'], + ['Passwortgeschützte Backups müssen als .mhu gespeichert werden', 'Password-protected backups must be saved as .mhu files'], + ['Sicherer Zugangsdaten-Speicher ist nicht verfügbar', 'Secure credential storage is unavailable'], + ['Zugangsdaten konnten nicht sicher verschlüsselt werden', 'Credentials could not be encrypted securely'], + ['Gespeicherte Zugangsdaten konnten nicht entschlüsselt werden', 'Stored credentials could not be decrypted'], + ['Zugangsdaten gesperrt', 'Credentials locked'], + ['Unsichere Klartext-Speicherung erlauben', 'Allow insecure plaintext storage'], + ['Nur verwenden, wenn der sichere Betriebssystem-Speicher dauerhaft nicht verfügbar ist. Passwörter und API-Keys liegen dann lesbar in der Konfigurationsdatei.', 'Use only when secure operating system storage is permanently unavailable. Passwords and API keys will then be readable in the configuration file.'], + ['Sicher verfügbar', 'Securely available'], + ['Nicht verfügbar', 'Unavailable'], + ['Zugangsdaten unverschlüsselt speichern?', 'Store credentials without encryption?'], + ['Passwörter und API-Keys werden dann im Klartext auf diesem PC gespeichert. Andere Benutzer oder Programme mit Dateizugriff können sie lesen.', 'Passwords and API keys will then be stored as plaintext on this PC. Other users or programs with file access can read them.'], + ['Klartext-Speicherung erlauben', 'Allow plaintext storage'], ['Die Verlaufsdatei ist beschädigt und wurde nicht verändert', 'The history file is damaged and was not changed'], ['Verlauf und Aufbewahrung konnten nicht konsistent gespeichert werden', 'History and retention could not be saved consistently'], ['Kein Ordnerpfad angegeben', 'No folder path was provided'], @@ -488,6 +515,14 @@ ['SHA-512 Prüfung fehlgeschlagen', 'SHA-512 verification failed'], ['Vorbereitetes Update ist unvollständig', 'The prepared update is incomplete'], ['Kein Setup-Asset im Release gefunden', 'No setup asset was found in the release'], + ['Prüfsummen-Metadaten fehlen', 'Checksum metadata is missing'], + ['Prüfsummen-Metadaten sind unvollständig', 'Checksum metadata is incomplete'], + ['Prüfsummen-Metadaten enthalten keine gültige SHA-512-Prüfsumme', 'Checksum metadata does not contain a valid SHA-512 checksum'], + ['Prüfsummen-Metadaten gehören zu einer anderen Version', 'Checksum metadata belongs to a different version'], + ['Prüfsummen-Metadaten gehören nicht zum ausgewählten Installer', 'Checksum metadata does not belong to the selected installer'], + ['Prüfsummen-Metadaten enthalten eine abweichende Dateigröße', 'Checksum metadata contains a different file size'], + ['Heruntergeladene Datei hat eine abweichende Größe', 'The downloaded file has a different size'], + ['Die installierte Anwendung ist nicht gültig digital signiert', 'The installed application is not validly digitally signed'], ['Download hängt — seit 45 s keine Daten (Netzwerk/Server überlastet). Bitte laufende Uploads stoppen und erneut versuchen.', 'The download stalled because no data was received for 45 seconds. Stop active uploads and try again.'], ['Datei nicht gefunden', 'File not found'], ['Netzwerkfehler', 'Network error'], @@ -592,6 +627,7 @@ const patterns = target === 'en' ? [ [/^Update v(.+) verfügbar$/, 'Update v$1 available'], + [/^Wiederhergestellte Warteschlange startet in (.+) s \((.+) Jobs\)\.$/, 'Restored queue starts in $1 s ($2 jobs).'], [/^Login ok, Upload-Form bereit \(Dateifeld: (.+)\)$/, 'Login successful, upload form ready (file field: $1)'], [/^Klartext-Backup ist kein gültiges JSON: (.+)$/, 'Plain JSON backup is not valid JSON: $1'], [/^Export fehlgeschlagen: (.+)$/, 'Export failed: $1'], @@ -619,6 +655,8 @@ [/^VOE Upload-Fehler: (.+)$/, 'VOE upload error: $1'], [/^VOE: Upload-Server Antwort war kein JSON: (.+)$/, 'VOE: The upload server response was not JSON: $1'], [/^Download fehlgeschlagen: (.+)$/, 'Download failed: $1'], + [/^Prüfsummen-Metadaten konnten nicht geladen werden: (.+)$/, 'Checksum metadata could not be loaded: $1'], + [/^Upload zu (.+) wurde nicht bestätigt$/, 'The upload to $1 was not confirmed'], [/^Update-Server Antwort war kein JSON (.+)$/, 'Update server response was not JSON $1'], [/^(\d+) Links kopiert$/, '$1 links copied'], [/^(\d+) Link kopiert$/, '$1 link copied'], diff --git a/renderer/index.html b/renderer/index.html index edb28b0..4deb5ca 100644 --- a/renderer/index.html +++ b/renderer/index.html @@ -553,6 +553,11 @@ Fehler 0 +
@@ -621,6 +626,10 @@
+