From 532426db8ca4925443301c551159aaaa679b3425 Mon Sep 17 00:00:00 2001 From: digitalnomad91 Date: Fri, 19 Sep 2025 00:42:05 -0500 Subject: [PATCH 1/3] feat: replace YouTube.js with yt-dlp for audio downloads - Complete rewrite of downloader plugin to use yt-dlp external tool - Improved error handling and user feedback with enhanced logging - Better progress tracking during downloads and conversions - Added file existence checking with skipExisting option - Enhanced metadata embedding (thumbnail, ID3 tags, artist/title) - Streamlined playlist download functionality - Added configurable yt-dlp path in advanced settings - Higher quality audio downloads (320K bitrate) - More robust download process with better error reporting --- src/plugins/downloader/index.ts | 6 + src/plugins/downloader/main/index.ts | 1088 +++++++++++++------------- src/plugins/downloader/menu.ts | 30 + 3 files changed, 562 insertions(+), 562 deletions(-) diff --git a/src/plugins/downloader/index.ts b/src/plugins/downloader/index.ts index 914f7db76b..4351a0f6f1 100644 --- a/src/plugins/downloader/index.ts +++ b/src/plugins/downloader/index.ts @@ -22,6 +22,9 @@ export type DownloaderPluginConfig = { customPresetSetting: Preset; skipExisting: boolean; playlistMaxItems?: number; + advanced?: { + ytDlpPath?: string; + }; }; export const defaultConfig: DownloaderPluginConfig = { @@ -38,6 +41,9 @@ export const defaultConfig: DownloaderPluginConfig = { customPresetSetting: DefaultPresetList['mp3 (256kbps)'], // Presets skipExisting: false, playlistMaxItems: undefined, + advanced: { + ytDlpPath: undefined, + }, }; export default createPlugin({ diff --git a/src/plugins/downloader/main/index.ts b/src/plugins/downloader/main/index.ts index 902f41abd0..40d1d57d2e 100644 --- a/src/plugins/downloader/main/index.ts +++ b/src/plugins/downloader/main/index.ts @@ -1,83 +1,174 @@ -import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; -import { join } from 'node:path'; -import { randomBytes } from 'node:crypto'; +import path from 'node:path'; +import { homedir } from 'node:os'; +import { existsSync, mkdirSync } from 'node:fs'; +import { spawn } from 'node:child_process'; import { app, type BrowserWindow, dialog, ipcMain } from 'electron'; -import { Innertube, UniversalCache, Utils, YTNodes } from 'youtubei.js'; import is from 'electron-is'; -import filenamify from 'filenamify'; -import { Mutex } from 'async-mutex'; -import * as NodeID3 from 'node-id3'; -import { BG, type BgConfig } from 'bgutils-js'; -import { lazy } from 'lazy-var'; import { - cropMaxWidth, getFolder, sendFeedback as sendFeedback_, setBadge, } from './utils'; import { registerCallback, - cleanupName, - getImage, - MediaType, type SongInfo, SongInfoEvent, } from '@/providers/song-info'; -import { getNetFetchAsFetch } from '@/plugins/utils/main'; import { t } from '@/i18n'; -import { DefaultPresetList, type Preset, YoutubeFormatList } from '../types'; - import type { DownloaderPluginConfig } from '../index'; import type { BackendContext } from '@/types/contexts'; import type { GetPlayerResponse } from '@/types/get-player-response'; -import type { FormatOptions } from 'node_modules/youtubei.js/dist/src/types'; -import type { VideoInfo } from 'node_modules/youtubei.js/dist/src/parser/youtube'; -import type { PlayerErrorMessage } from 'node_modules/youtubei.js/dist/src/parser/nodes'; -import type { - TrackInfo, - Playlist, -} from 'node_modules/youtubei.js/dist/src/parser/ytmusic'; - -type CustomSongInfo = SongInfo & { trackId?: string }; - -const ffmpeg = lazy(async () => - (await import('@ffmpeg.wasm/main')).createFFmpeg({ - log: false, - logger() {}, // Console.log, - progress() {}, // Console.log, - }), -); -const ffmpegMutex = new Mutex(); - -let yt: Innertube; -let win: BrowserWindow; -let playingUrl: string; -const isYouTubeMusicPremium = async () => { - // If signed out, it is understood as non-premium - const isSignedIn = (await win.webContents.executeJavaScript( - '!!yt.config_.LOGGED_IN', - )) as boolean; +// Helper to send OS notification if notifications plugin is enabled +async function sendOsNotification(title: string, body: string) { + try { + // First try to use Electron's built-in Notification API + const { Notification } = await import('electron'); + + if (Notification.isSupported()) { + const notification = new Notification({ + title, + body, + silent: false, + }); + notification.show(); + return true; + } + } catch (error) { + logToFrontend('warn', '⚠️ Could not send notification:', error); + } + return false; +} - if (!isSignedIn) return false; +// Helper to clean URL and convert music.youtube.com to youtube.com for better yt-dlp compatibility +function cleanAndConvertUrl(url: string): string { + try { + const urlObj = new URL(url); - // If signed in, check if the upgrade button is present - const upgradeBtnIconPathData = (await win.webContents.executeJavaScript( - 'document.querySelector(\'iron-iconset-svg[name="yt-sys-icons"] #youtube_music_monochrome\')?.firstChild?.getAttribute("d")?.substring(0, 15)', - )) as string | null; + // Convert music.youtube.com to youtube.com + if (urlObj.hostname === 'music.youtube.com') { + urlObj.hostname = 'youtube.com'; + } - // Fallback to non-premium if the icon is not found - if (!upgradeBtnIconPathData) return false; + // Remove playlist parameters to prevent downloading entire playlists + const params = new URLSearchParams(urlObj.search); + if (params.has('list')) { + params.delete('list'); + } + if (params.has('index')) { + params.delete('index'); + } - const upgradeButton = `ytmusic-guide-entry-renderer:has(> tp-yt-paper-item > yt-icon path[d^="${upgradeBtnIconPathData}"])`; + urlObj.search = params.toString(); + return urlObj.toString(); + } catch (error) { + console.warn('[Downloader] Failed to clean URL, using original:', error); + return url; + } +} - return (await win.webContents.executeJavaScript( - `!document.querySelector('${upgradeButton}')`, - )) as boolean; -}; +// Cached yt-dlp path to reduce path checks and verbose logging +let cachedYtDlpPath: string | undefined; +let cachedConfig: DownloaderPluginConfig | null = null; + +// Helper function to log both to backend console and frontend +function logToFrontend( + level: 'info' | 'warn' | 'error', + message: string, + ...args: unknown[] +) { + const formattedMessage = args.length > 0 + ? `${message} ${args.join(' ')}` + : message; + + console[level](`[Downloader] ${formattedMessage}`); + + // Also log to frontend for production debugging + try { + win?.webContents?.executeJavaScript( + `console.${level}('[Downloader] ${formattedMessage.replace(/'/g, "\\'")}');` + ); + } catch { + // Ignore errors if window not available + } +} + +// Helper to find yt-dlp path, with caching to reduce verbose logging +function getYtDlpPath(customPath?: string): string | undefined { + // Check cache first to avoid redundant file system checks + if (cachedYtDlpPath && cachedConfig?.advanced?.ytDlpPath === customPath) { + return cachedYtDlpPath; + } + + const checkedPaths: string[] = []; + if (customPath) { + checkedPaths.push(`[custom] ${customPath}`); + if (existsSync(customPath)) { + // Only log yt-dlp path once when plugin loads, not for every download + if (!cachedYtDlpPath) { + logToFrontend('info', 'πŸ”§ Using custom yt-dlp path:', customPath); + } + cachedYtDlpPath = customPath; + return customPath; + } + } + + if (is.windows()) { + const candidates = [ + 'C:/yt-dlp.exe', + path.join(homedir(), 'Downloads', 'yt-dlp.exe'), + 'C:/utils/yt-dlp.exe', + ]; + for (const p of candidates) { + checkedPaths.push(p); + if (existsSync(p)) { + // Only log yt-dlp path once when plugin loads, not for every download + if (!cachedYtDlpPath) { + logToFrontend('info', 'πŸ”§ Found yt-dlp at:', p); + } + cachedYtDlpPath = p; + return p; + } + } + } else if (is.linux()) { + checkedPaths.push('/usr/bin/yt-dlp'); + if (existsSync('/usr/bin/yt-dlp')) { + // Only log yt-dlp path once when plugin loads, not for every download + if (!cachedYtDlpPath) { + logToFrontend('info', 'πŸ”§ Found yt-dlp at: /usr/bin/yt-dlp'); + } + cachedYtDlpPath = '/usr/bin/yt-dlp'; + return '/usr/bin/yt-dlp'; + } + } else if (is.macOS()) { + const macCandidates = [ + '/usr/local/bin/yt-dlp', + '/opt/homebrew/bin/yt-dlp', + ]; + for (const p of macCandidates) { + checkedPaths.push(p); + if (existsSync(p)) { + // Only log yt-dlp path once when plugin loads, not for every download + if (!cachedYtDlpPath) { + logToFrontend('info', 'πŸ”§ Found yt-dlp at:', p); + } + cachedYtDlpPath = p; + return p; + } + } + } + + logToFrontend('warn', '⚠️ yt-dlp not found. Paths checked:', checkedPaths); + cachedYtDlpPath = undefined; + return undefined; +} + +let win: BrowserWindow; +let playingUrl: string; +let config: DownloaderPluginConfig; const sendError = (error: Error, source?: string) => { win.setProgressBar(-1); // Close progress bar @@ -93,29 +184,33 @@ const sendError = (error: Error, source?: string) => { : ''; const message = `${error.toString()}${songNameMessage}${cause}`; - console.error(message); + // Print full error to console for debugging + console.error('[Downloader] Error:', message); + if (error instanceof Error && error.stack) { + console.error(error.stack); + } console.trace(error); + + // Try to extract command info from error message + let commandInfo = ''; + const match = message.match(/Command: ([^\n]+)/); + if (match) { + commandInfo = `\n\nCommand attempted: ${match[1]}`; + } + + // Show user-friendly error dialog dialog.showMessageBox(win, { - type: 'info', + type: 'error', buttons: [t('plugins.downloader.backend.dialog.error.buttons.ok')], title: t('plugins.downloader.backend.dialog.error.title'), message: t('plugins.downloader.backend.dialog.error.message'), - detail: message, + detail: + message + + commandInfo + + '\n\nIf this is a yt-dlp error, please check the path in the Downloader plugin settings.', }); }; -export const getCookieFromWindow = async (win: BrowserWindow) => { - return ( - await win.webContents.session.cookies.get({ - url: 'https://music.youtube.com', - }) - ) - .map((it) => it.name + '=' + it.value) - .join(';'); -}; - -let config: DownloaderPluginConfig; - export const onMainLoad = async ({ window: _win, getConfig, @@ -124,71 +219,8 @@ export const onMainLoad = async ({ win = _win; config = await getConfig(); - yt = await Innertube.create({ - cache: new UniversalCache(false), - cookie: await getCookieFromWindow(win), - generate_session_locally: true, - fetch: getNetFetchAsFetch(), - }); - - const requestKey = 'O43z0dpjhgX20SCx4KAo'; - const visitorData = yt.session.context.client.visitorData; - - if (visitorData) { - const cleanUp = (context: Partial) => { - delete context.window; - delete context.document; - }; - - try { - const [width, height] = win.getSize(); - // emulate jsdom using linkedom - const window = new (await import('happy-dom')).Window({ - width, - height, - console, - }); - const document = window.document; - - Object.assign(globalThis, { - window, - document, - }); - - const bgConfig: BgConfig = { - fetch: getNetFetchAsFetch(), - globalObj: globalThis, - identifier: visitorData, - requestKey, - }; - - const bgChallenge = await BG.Challenge.create(bgConfig); - const interpreterJavascript = - bgChallenge?.interpreterJavascript - .privateDoNotAccessOrElseSafeScriptWrappedValue; - - if (interpreterJavascript) { - // This is a workaround to run the interpreterJavascript code - // Maybe there is a better way to do this (e.g. https://github.com/Siubaak/sval ?) - // eslint-disable-next-line @typescript-eslint/no-implied-eval,@typescript-eslint/no-unsafe-call - new Function(interpreterJavascript)(); - - const poTokenResult = await BG.PoToken.generate({ - program: bgChallenge.program, - globalName: bgChallenge.globalName, - bgConfig, - }).finally(() => { - cleanUp(globalThis); - }); - - yt.session.po_token = poTokenResult.poToken; - } else { - cleanUp(globalThis); - } - } catch { - cleanUp(globalThis); - } - } + // Update cache when config changes + cachedConfig = config; ipc.handle('download-song', (url: string) => downloadSong(url)); ipc.on('ytmd:video-src-changed', (data: GetPlayerResponse) => { @@ -203,6 +235,13 @@ export const onMainLoad = async ({ export const onConfigChange = (newConfig: DownloaderPluginConfig) => { config = newConfig; + // Update cache when config changes + cachedConfig = newConfig; + + // Reset yt-dlp path cache if custom path changed + if (cachedConfig?.advanced?.ytDlpPath !== newConfig.advanced?.ytDlpPath) { + cachedYtDlpPath = undefined; + } }; export async function downloadSong( @@ -307,7 +346,7 @@ async function downloadSongUnsafe( idOrUrl: string, setName: (name: string) => void, playlistFolder: string | undefined = undefined, - trackId: string | undefined = undefined, + _trackId: string | undefined = undefined, increasePlaylistProgress: (value: number) => void = () => {}, ) { const sendFeedback = (message: unknown, progress?: number) => { @@ -319,297 +358,225 @@ async function downloadSongUnsafe( } }; - sendFeedback(t('plugins.downloader.backend.feedback.downloading'), 2); + sendFeedback(t('plugins.downloader.backend.feedback.downloading'), 0.1); - let id: string | null; + let url: string; if (isId) { - id = idOrUrl; + url = `https://youtube.com/watch?v=${idOrUrl}`; } else { - id = getVideoId(idOrUrl); - if (typeof id !== 'string') - throw new Error( - t('plugins.downloader.backend.feedback.video-id-not-found'), - ); + // Clean up the URL and convert music.youtube.com to youtube.com + url = cleanAndConvertUrl(idOrUrl); } - let info: TrackInfo | VideoInfo = await yt.music.getInfo(id); - - if (!info) { - throw new Error( - t('plugins.downloader.backend.feedback.video-id-not-found'), - ); - } - - const metadata = getMetadata(info); - if (metadata.album === 'N/A') { - metadata.album = ''; - } - - metadata.trackId = trackId; - - const dir = - playlistFolder || config.downloadFolder || app.getPath('downloads'); - const name = `${metadata.artist ? `${metadata.artist} - ` : ''}${ - metadata.title - }`; - setName(name); - - let playabilityStatus = info.playability_status; - let bypassedResult = null; - if (playabilityStatus?.status === 'LOGIN_REQUIRED') { - // Try to bypass the age restriction - bypassedResult = await getAndroidTvInfo(id); - playabilityStatus = bypassedResult.playability_status; - - if (playabilityStatus?.status === 'LOGIN_REQUIRED') { - throw new Error( - `[${playabilityStatus.status}] ${playabilityStatus.reason}`, - ); - } - - info = bypassedResult; + const dir = playlistFolder || config.downloadFolder || app.getPath('downloads'); + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true }); } - if (playabilityStatus?.status === 'UNPLAYABLE') { - const errorScreen = - playabilityStatus.error_screen as PlayerErrorMessage | null; + // Find yt-dlp path using cached value or search + const ytDlpPath = getYtDlpPath(config.advanced?.ytDlpPath); + if (!ytDlpPath) { + const allPaths = [ + config.advanced?.ytDlpPath + ? `[custom] ${config.advanced.ytDlpPath}` + : null, + ...(is.windows() + ? [ + 'C:/yt-dlp.exe', + path.join(homedir(), 'Downloads', 'yt-dlp.exe'), + 'C:/utils/yt-dlp.exe', + ] + : is.linux() + ? ['/usr/bin/yt-dlp'] + : is.macOS() + ? ['/usr/local/bin/yt-dlp', '/opt/homebrew/bin/yt-dlp'] + : []), + ].filter(Boolean); + + logToFrontend('error', '❌ yt-dlp not found. Paths checked:', allPaths); throw new Error( - `[${playabilityStatus.status}] ${errorScreen?.reason.text}: ${errorScreen?.subreason.text}`, + 'yt-dlp executable not found.\nPaths checked:\n' + + allPaths.join('\n') + + '\nPlease set the path in the Downloader plugin menu.', ); } - const selectedPreset = config.selectedPreset ?? 'mp3 (256kbps)'; - let presetSetting: Preset; - if (selectedPreset === 'Custom') { - presetSetting = config.customPresetSetting ?? DefaultPresetList['Custom']; - } else if (selectedPreset === 'Source') { - presetSetting = DefaultPresetList['Source']; - } else { - presetSetting = DefaultPresetList['mp3 (256kbps)']; - } - - const downloadOptions: FormatOptions = { - type: (await isYouTubeMusicPremium()) ? 'audio' : 'video+audio', // Audio, video or video+audio - quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on. - format: 'any', // Media container format - }; - - const format = info.chooseFormat(downloadOptions); - - let targetFileExtension: string; - if (!presetSetting?.extension) { - targetFileExtension = - YoutubeFormatList.find((it) => it.itag === format.itag)?.container ?? - 'mp3'; - } else { - targetFileExtension = presetSetting?.extension ?? 'mp3'; - } - - let filename = filenamify(`${name}.${targetFileExtension}`, { - replacement: '_', - maxLength: 255, - }); - if (!is.macOS()) { - filename = filename.normalize('NFC'); - } - const filePath = join(dir, filename); - - if (config.skipExisting && existsSync(filePath)) { - sendFeedback(null, -1); - return; - } - - const stream = await info.download(downloadOptions); + // Enhanced output template with artist and title, higher quality, metadata + const outTemplate = `${dir}/%(artist)s - %(title)s.%(ext)s`; - console.info( - t('plugins.downloader.backend.feedback.download-info', { - artist: metadata.artist, - title: metadata.title, - videoId: metadata.videoId, - }), - ); + // Check if file already exists when skipExisting is enabled + if (config.skipExisting) { + try { + // Get the expected filename by running yt-dlp with --print option first + const infoArgs = ['--print', '%(artist)s - %(title)s.%(ext)s', url]; + const infoProcess = spawn(ytDlpPath, infoArgs, { + stdio: ['pipe', 'pipe', 'pipe'], + shell: false, + }); - const iterableStream = Utils.streamToIterable(stream); + let infoOutput = ''; + infoProcess.stdout?.on('data', (data: Buffer) => { + infoOutput += data.toString(); + }); - if (!existsSync(dir)) { - mkdirSync(dir); - } + const infoExitCode = await new Promise((resolve) => { + infoProcess.on('close', resolve); + }); - let fileBuffer = await iterableStreamToProcessedUint8Array( - iterableStream, - targetFileExtension, - metadata, - presetSetting?.ffmpegArgs ?? [], - format.content_length ?? 0, - sendFeedback, - increasePlaylistProgress, - ); + if (infoExitCode === 0) { + const expectedFilename = infoOutput.trim().replace('.webm', '.mp3').replace('.m4a', '.mp3'); + const expectedPath = path.join(dir, expectedFilename); - if (fileBuffer && targetFileExtension === 'mp3') { - fileBuffer = await writeID3( - Buffer.from(fileBuffer), - metadata, - sendFeedback, - ); - } - - if (fileBuffer) { - writeFileSync(filePath, fileBuffer); + if (existsSync(expectedPath)) { + logToFrontend('info', '⏭️ File already exists, skipping:', expectedPath); + sendFeedback(t('plugins.downloader.backend.feedback.file-already-exists'), -1); + setName(expectedPath); + return; // Skip download + } + } + } catch (error) { + logToFrontend('warn', '⚠️ Could not check for existing file:', error); + // Continue with download if check fails + } } - sendFeedback(null, -1); - console.info( - t('plugins.downloader.backend.feedback.done', { - filePath, - }), - ); -} + // Enhanced args with higher quality and metadata embedding (NO separate thumbnail downloads) + const args = [ + '-x', + '--audio-format', + 'mp3', + '--audio-quality', + '320K', // Higher bitrate + '--embed-thumbnail', // Embed album art into the MP3 + '--embed-metadata', // Embed ID3 tags + '--add-metadata', // Additional metadata + '--convert-thumbnails', + 'jpg', // Convert to standard format (for embedded only, no separate files) + + '-o', + outTemplate, + url, + ]; -async function downloadChunks( - stream: AsyncGenerator, - contentLength: number, - sendFeedback: (str: string, value?: number) => void, - increasePlaylistProgress: (value: number) => void = () => {}, -) { - const chunks = []; - let downloaded = 0; - for await (const chunk of stream) { - downloaded += chunk.length; - chunks.push(chunk); - const ratio = downloaded / contentLength; - const progress = Math.floor(ratio * 100); - sendFeedback( - t('plugins.downloader.backend.feedback.download-progress', { - percent: progress, - }), - ratio, - ); - // 15% for download, 85% for conversion - // This is a very rough estimate, trying to make the progress bar look nice - increasePlaylistProgress(ratio * 0.15); + // Add skip existing flag if enabled + if (config.skipExisting) { + args.push('--no-overwrites'); } - return chunks; -} - -async function iterableStreamToProcessedUint8Array( - stream: AsyncGenerator, - extension: string, - metadata: CustomSongInfo, - presetFfmpegArgs: string[], - contentLength: number, - sendFeedback: (str: string, value?: number) => void, - increasePlaylistProgress: (value: number) => void = () => {}, -): Promise { - sendFeedback(t('plugins.downloader.backend.feedback.loading'), 2); // Indefinite progress bar after download - const safeVideoName = randomBytes(32).toString('hex'); + // Enhanced colored logging with unicode icons + logToFrontend('info', '⬇️ Starting download...'); + logToFrontend('info', '🎡 URL:', url); - return await ffmpegMutex.runExclusive(async () => { - try { - const ffmpegInstance = await ffmpeg.get(); - if (!ffmpegInstance.isLoaded()) { - await ffmpegInstance.load(); - } + setName(url); // We'll update this when we get the actual filename - sendFeedback(t('plugins.downloader.backend.feedback.preparing-file')); - ffmpegInstance.FS( - 'writeFile', - safeVideoName, - Buffer.concat( - await downloadChunks( - stream, - contentLength, - sendFeedback, - increasePlaylistProgress, - ), - ), + try { + const ytDlpProcess = spawn(ytDlpPath, args, { + stdio: ['pipe', 'pipe', 'pipe'], + shell: false, // Only spawn once, no shell wrapping + }); + + let output = ''; + let errorOutput = ''; + let downloadedFile = ''; + + ytDlpProcess.stdout?.on('data', (data: Buffer) => { + const chunk = data.toString(); + output += chunk; + + // Parse for actual downloaded filename to show correct path in logs + const destinationMatch = chunk.match( + /\[ExtractAudio\] Destination: (.+)/, ); - - sendFeedback(t('plugins.downloader.backend.feedback.converting')); - - ffmpegInstance.setProgress(({ ratio }) => { - sendFeedback( - t('plugins.downloader.backend.feedback.conversion-progress', { - percent: Math.floor(ratio * 100), - }), - ratio, - ); - increasePlaylistProgress(0.15 + ratio * 0.85); - }); - - const safeVideoNameWithExtension = `${safeVideoName}.${extension}`; - try { - await ffmpegInstance.run( - '-i', - safeVideoName, - ...presetFfmpegArgs, - ...getFFmpegMetadataArgs(metadata), - safeVideoNameWithExtension, - ); - } finally { - ffmpegInstance.FS('unlink', safeVideoName); + if (destinationMatch) { + downloadedFile = destinationMatch[1]; + logToFrontend('info', 'πŸ“ Saving to:', downloadedFile); + setName(downloadedFile); } - sendFeedback(t('plugins.downloader.backend.feedback.saving')); + // Parse progress with multiple patterns for better compatibility + let progressMatch = chunk.match(/(\d+(?:\.\d+)?)%/); // Look for percentage with optional decimal + if (!progressMatch) { + progressMatch = output.match(/(\d+(?:\.\d+)?)%/); // Also check accumulated output + } - try { - return ffmpegInstance.FS('readFile', safeVideoNameWithExtension); - } finally { - ffmpegInstance.FS('unlink', safeVideoNameWithExtension); + if (progressMatch) { + const progress = parseFloat(progressMatch[1]) / 100; + if (!isNaN(progress) && progress > 0) { + logToFrontend('info', `πŸ“Š Progress: ${Math.floor(progress * 100)}%`); + sendFeedback( + t('plugins.downloader.backend.feedback.download-progress', { + percent: Math.floor(progress * 100), + }), + progress, + ); + increasePlaylistProgress(progress); + } } - } catch (error: unknown) { - sendError(error as Error, safeVideoName); - } - return null; - }); -} -const getCoverBuffer = async (url: string) => { - const nativeImage = cropMaxWidth(await getImage(url)); - return nativeImage && !nativeImage.isEmpty() ? nativeImage.toPNG() : null; -}; + // Detect conversion phase + if (chunk.includes('[ExtractAudio]') || chunk.includes('Converting')) { + sendFeedback(t('plugins.downloader.backend.feedback.converting')); + } + }); -async function writeID3( - buffer: Buffer, - metadata: CustomSongInfo, - sendFeedback: (str: string, value?: number) => void, -) { - try { - sendFeedback(t('plugins.downloader.backend.feedback.writing-id3')); - const tags: NodeID3.Tags = {}; + ytDlpProcess.stderr?.on('data', (data: Buffer) => { + errorOutput += data.toString(); + }); - // Create the metadata tags - tags.title = metadata.title; - tags.artist = metadata.artist; + const exitCode = await new Promise((resolve) => { + ytDlpProcess.on('close', resolve); + }); - if (metadata.album) { - tags.album = metadata.album; + if (exitCode !== 0) { + throw new Error( + `yt-dlp failed with exit code ${exitCode}: ${errorOutput}` + + `\nCommand: ${ytDlpPath} ${args.join(' ')}`, + ); } - const coverBuffer = await getCoverBuffer(metadata.imageSrc ?? ''); - if (coverBuffer) { - tags.image = { - mime: 'image/png', - type: { - id: NodeID3.TagConstants.AttachedPicture.PictureType.FRONT_COVER, - }, - description: 'thumbnail', - imageBuffer: coverBuffer, - }; + sendFeedback(null, -1); + + // Try to determine the final file location more accurately + let finalFile = downloadedFile; + if (!finalFile || finalFile === '') { + // Try to find the file by looking for mp3 files in the directory + try { + const fs = await import('fs'); + const files = fs.readdirSync(dir); + const mp3Files = files.filter((f) => f.endsWith('.mp3')); + if (mp3Files.length > 0) { + // Sort by modification time, get the newest + const fullPaths = mp3Files.map((f) => path.join(dir, f)); + const newest = fullPaths.reduce((a, b) => { + const aStat = fs.statSync(a); + const bStat = fs.statSync(b); + return aStat.mtime > bStat.mtime ? a : b; + }); + finalFile = newest; + logToFrontend('info', 'πŸ” Found downloaded file:', finalFile); + } + } catch (error) { + logToFrontend('warn', '⚠️ Could not determine final file location:', error); + } } - if (metadata.trackId) { - tags.trackNumber = metadata.trackId; + if (!finalFile) { + finalFile = 'Unknown location (check download folder)'; } - return NodeID3.write(tags, buffer); - } catch (error: unknown) { - sendError(error as Error, `${metadata.artist} - ${metadata.title}`); - return null; + logToFrontend('info', 'βœ… Download complete!'); + logToFrontend('info', 'πŸ“‚ File saved:', finalFile); + sendOsNotification('Download complete!', `Downloaded: ${path.basename(finalFile)}`).catch(() => {}); + } catch (error) { + logToFrontend('error', '❌ Download failed:', error); + sendOsNotification('Download failed!', String(error)).catch(() => {}); + throw new Error(`Download failed: ${String(error)}`); } } export async function downloadPlaylist(givenUrl?: string | URL) { + logToFrontend('info', '🎡 Starting playlist download...'); + try { givenUrl = new URL(givenUrl ?? ''); } catch { @@ -620,13 +587,21 @@ export async function downloadPlaylist(givenUrl?: string | URL) { getPlaylistID(givenUrl) || getPlaylistID(new URL(playingUrl)); if (!playlistId) { + logToFrontend('error', '❌ No playlist ID found'); sendError( new Error(t('plugins.downloader.backend.feedback.playlist-id-not-found')), ); return; } - const sendFeedback = (message?: unknown) => sendFeedback_(win, message); + logToFrontend('info', 'πŸ†” Playlist ID:', playlistId); + + const sendFeedback = (message?: unknown, progress?: number) => { + sendFeedback_(win, message); + if (typeof progress === 'number' && !isNaN(progress)) { + win.setProgressBar(progress); + } + }; console.log( t('plugins.downloader.backend.feedback.trying-to-get-playlist-id', { @@ -634,186 +609,202 @@ export async function downloadPlaylist(givenUrl?: string | URL) { }), ); sendFeedback(t('plugins.downloader.backend.feedback.getting-playlist-info')); - let playlist: Playlist; - const items: YTNodes.MusicResponsiveListItem[] = []; - try { - playlist = await yt.music.getPlaylist(playlistId); - if (playlist?.items) { - const filteredItems = playlist.items.filter( - (item): item is YTNodes.MusicResponsiveListItem => - item instanceof YTNodes.MusicResponsiveListItem, - ); - - items.push(...filteredItems); - } - } catch (error: unknown) { - sendError( - Error( - t('plugins.downloader.backend.feedback.playlist-is-mix-or-private', { - error: String(error), - }), - ), - ); - return; - } - if (!playlist || !playlist.items || playlist.items.length === 0) { + const dir = getFolder(config.downloadFolder ?? ''); + const playlistUrl = `https://music.youtube.com/playlist?list=${playlistId}`; + + logToFrontend('info', 'πŸ“ Download directory:', dir); + logToFrontend('info', 'πŸ”— Playlist URL:', playlistUrl); + + // Find yt-dlp path using cached and fallback logic + const ytDlpPath = getYtDlpPath(config.advanced?.ytDlpPath); + if (!ytDlpPath) { + const allPaths = [ + config.advanced?.ytDlpPath ? `[custom] ${config.advanced.ytDlpPath}` : null, + ...(is.windows() + ? [ + 'C:/yt-dlp.exe', + path.join(homedir(), 'Downloads', 'yt-dlp.exe'), + 'C:/utils/yt-dlp.exe', + ] + : is.linux() + ? ['/usr/bin/yt-dlp'] + : is.macOS() + ? ['/usr/local/bin/yt-dlp', '/opt/homebrew/bin/yt-dlp'] + : []), + ].filter(Boolean); + + logToFrontend('error', '❌ yt-dlp not found for playlist. Paths checked:', allPaths); sendError( - new Error(t('plugins.downloader.backend.feedback.playlist-is-empty')), + new Error( + 'yt-dlp executable not found.\nPaths checked:\n' + + allPaths.join('\n') + + '\nPlease set the path in the Downloader plugin menu.' + ) ); return; } - const normalPlaylistTitle = - playlist.header && 'title' in playlist.header - ? playlist.header?.title?.text - : undefined; - const playlistTitle = - normalPlaylistTitle ?? - playlist.page.contents_memo - ?.get('MusicResponsiveListItemFlexColumn') - ?.at(2) - ?.as(YTNodes.MusicResponsiveListItemFlexColumn)?.title?.text ?? - 'NO_TITLE'; - const isAlbum = !normalPlaylistTitle; - - while (playlist.has_continuation) { - playlist = await playlist.getContinuation(); - - const filteredItems = playlist.items.filter( - (item): item is YTNodes.MusicResponsiveListItem => - item instanceof YTNodes.MusicResponsiveListItem, - ); - - items.push(...filteredItems); - } + const args = [ + '-x', + '--audio-format', + 'mp3', + '--audio-quality', + '320K', // Higher bitrate + '--embed-thumbnail', // Embed album art + '--embed-metadata', // Embed ID3 tags + '--add-metadata', // Additional metadata + '-o', + `${dir}/%(playlist_title)s/%(title)s.%(ext)s`, + playlistUrl, + ]; - if (items.length === 1) { - sendFeedback( - t('plugins.downloader.backend.feedback.playlist-has-only-one-song'), - ); - await downloadSongFromId(items.at(0)!.id!); - return; + // Add skip existing flag if enabled + if (config.skipExisting) { + args.push('--no-overwrites'); } - let safePlaylistTitle = filenamify(playlistTitle, { replacement: ' ' }); - if (!is.macOS()) { - safePlaylistTitle = safePlaylistTitle.normalize('NFC'); - } + try { + logToFrontend('info', 'πŸ’¬ Showing playlist download dialog...'); + + const dialogResult = await dialog.showMessageBox(win, { + type: 'info', + buttons: [ + t('plugins.downloader.backend.dialog.start-download-playlist.buttons.ok'), + 'Cancel' + ], + title: t('plugins.downloader.backend.dialog.start-download-playlist.title'), + message: t( + 'plugins.downloader.backend.dialog.start-download-playlist.message', + { + playlistTitle: playlistId, + }, + ), + detail: t( + 'plugins.downloader.backend.dialog.start-download-playlist.detail', + { + playlistSize: 'Unknown', + }, + ), + }); - const folder = getFolder(config.downloadFolder ?? ''); - const playlistFolder = join(folder, safePlaylistTitle); - if (existsSync(playlistFolder)) { - if (!config.skipExisting) { - sendError( - new Error( - t('plugins.downloader.backend.feedback.folder-already-exists', { - playlistFolder, - }), - ), - ); + // Check if user cancelled + if (dialogResult.response !== 0) { + logToFrontend('info', '❌ User cancelled playlist download'); + sendFeedback('Download cancelled', -1); return; } - } else { - mkdirSync(playlistFolder, { recursive: true }); - } - dialog.showMessageBox(win, { - type: 'info', - buttons: [ - t('plugins.downloader.backend.dialog.start-download-playlist.buttons.ok'), - ], - title: t('plugins.downloader.backend.dialog.start-download-playlist.title'), - message: t( - 'plugins.downloader.backend.dialog.start-download-playlist.message', - { - playlistTitle, - }, - ), - detail: t( - 'plugins.downloader.backend.dialog.start-download-playlist.detail', - { - playlistSize: items.length, - }, - ), - }); + logToFrontend('info', 'πŸš€ Starting yt-dlp process...'); + win.setProgressBar(0.1); // Start with indefinite bar - if (is.dev()) { - console.log( - t('plugins.downloader.backend.feedback.downloading-playlist', { - playlistTitle, - playlistSize: items.length, - playlistId, - }), - ); - } + logToFrontend('info', 'βš™οΈ yt-dlp command:', `${ytDlpPath} ${args.join(' ')}`); - win.setProgressBar(2); // Starts with indefinite bar + const ytDlpProcess = spawn(ytDlpPath, args, { + stdio: ['pipe', 'pipe', 'pipe'], + shell: false // Use shell: false for better stability + }); - setBadge(items.length); + let output = ''; + let errorOutput = ''; + let lastProgressUpdate = 0; - let counter = 1; + logToFrontend('info', 'πŸ“Š yt-dlp process started, PID:', ytDlpProcess.pid); - const progressStep = 1 / items.length; + ytDlpProcess.stdout?.on('data', (data: Buffer) => { + const chunk = data.toString(); + output += chunk; - const increaseProgress = (itemPercentage: number) => { - const currentProgress = (counter - 1) / (items.length ?? 1); - const newProgress = currentProgress + progressStep * itemPercentage; - win.setProgressBar(newProgress); - }; + // Log chunks for debugging (but limit the amount) + if (chunk.trim()) { + logToFrontend('info', 'πŸ“₯ yt-dlp output:', chunk.trim().substring(0, 200)); + } - try { - for (const song of items) { - sendFeedback( - t('plugins.downloader.backend.feedback.downloading-counter', { - current: counter, - total: items.length, - }), - ); - const trackId = isAlbum ? counter : undefined; - await downloadSongFromId( - song.id!, - playlistFolder, - trackId?.toString(), - increaseProgress, - ).catch((error) => - sendError( - new Error( - t('plugins.downloader.backend.feedback.error-while-downloading', { - author: song.author!.name, - title: song.title!, - error: String(error), - }), - ), - ), - ); + // Parse progress with multiple patterns for better compatibility + let progressMatch = chunk.match(/(\d+(?:\.\d+)?)%/); + if (!progressMatch) { + progressMatch = output.match(/(\d+(?:\.\d+)?)%/); + } + + if (progressMatch) { + const progress = parseFloat(progressMatch[1]) / 100; + if (!isNaN(progress) && progress > 0 && progress !== lastProgressUpdate) { + lastProgressUpdate = progress; + logToFrontend('info', `πŸ“Š Playlist progress: ${Math.floor(progress * 100)}%`); + sendFeedback(`Downloading... ${Math.floor(progress * 100)}%`, progress); + } + } + + // Detect conversion step + if (chunk.includes('[ExtractAudio]') || chunk.includes('Converting')) { + logToFrontend('info', 'πŸ”„ Converting audio...'); + sendFeedback('Converting to mp3...'); + } + + // Detect download completion for individual tracks + if (chunk.includes('[ExtractAudio] Destination:')) { + const filename = chunk.match(/\[ExtractAudio\] Destination: (.+)/); + if (filename) { + logToFrontend('info', 'βœ… Track completed:', path.basename(filename[1])); + } + } - win.setProgressBar(counter / items.length); - setBadge(items.length - counter); - counter++; + // Detect download start for tracks + if (chunk.includes('[youtube]') || chunk.includes('[download]')) { + if (chunk.includes('Downloading webpage')) { + logToFrontend('info', '🌐 Fetching track info...'); + } + } + }); + + ytDlpProcess.stderr?.on('data', (data: Buffer) => { + const errorChunk = data.toString(); + errorOutput += errorChunk; + logToFrontend('warn', '⚠️ yt-dlp stderr:', errorChunk.trim().substring(0, 200)); + }); + + ytDlpProcess.on('error', (error) => { + logToFrontend('error', 'πŸ’₯ yt-dlp process error:', error); + }); + + const exitCode = await new Promise((resolve) => { + ytDlpProcess.on('close', (code) => { + logToFrontend('info', '🏁 yt-dlp process closed with code:', code); + resolve(code || 0); + }); + }); + + if (exitCode !== 0) { + logToFrontend('error', '❌ yt-dlp failed with exit code:', exitCode); + logToFrontend('error', 'πŸ“„ Error output:', errorOutput); + throw new Error( + `yt-dlp failed with exit code ${exitCode}: ${errorOutput}` + + `\nCommand: ${ytDlpPath} ${args.join(' ')}` + ); } + + logToFrontend('info', 'πŸŽ‰ Playlist download completed successfully!'); + sendFeedback('Download complete!', -1); + sendOsNotification('Download complete!', `Saved to: ${dir}`).catch(() => {}); + logToFrontend('info', 'βœ… Playlist download complete!', `Saved to: ${dir}`); + console.info( + t('plugins.downloader.backend.feedback.done', { + filePath: dir, + }), + ); } catch (error: unknown) { + logToFrontend('error', 'πŸ’₯ Playlist download error:', error); + sendFeedback('Download failed!', -1); + sendOsNotification('Download failed!', String(error)).catch(() => {}); sendError(error as Error); } finally { + logToFrontend('info', '🧹 Cleaning up playlist download...'); win.setProgressBar(-1); // Close progress bar setBadge(0); // Close badge counter sendFeedback(); // Clear feedback } } -function getFFmpegMetadataArgs(metadata: CustomSongInfo) { - if (!metadata) { - return []; - } - - return [ - ...(metadata.title ? ['-metadata', `title=${metadata.title}`] : []), - ...(metadata.artist ? ['-metadata', `artist=${metadata.artist}`] : []), - ...(metadata.album ? ['-metadata', `album=${metadata.album}`] : []), - ...(metadata.trackId ? ['-metadata', `track=${metadata.trackId}`] : []), - ]; -} - // Playlist radio modifier needs to be cut from playlist ID const INVALID_PLAYLIST_MODIFIER = 'RDAMPL'; @@ -826,30 +817,3 @@ const getPlaylistID = (aURL?: URL): string | null | undefined => { return result; }; - -const getVideoId = (url: URL | string): string | null => { - return new URL(url).searchParams.get('v'); -}; - -const getMetadata = (info: TrackInfo): CustomSongInfo => ({ - videoId: info.basic_info.id!, - title: cleanupName(info.basic_info.title!), - artist: cleanupName(info.basic_info.author!), - album: info.player_overlays?.browser_media_session?.as( - YTNodes.BrowserMediaSession, - ).album?.text, - imageSrc: info.basic_info.thumbnail?.find((t) => !t.url.endsWith('.webp')) - ?.url, - views: info.basic_info.view_count!, - songDuration: info.basic_info.duration!, - mediaType: MediaType.Audio, -}); - -// This is used to bypass age restrictions -const getAndroidTvInfo = async (id: string): Promise => { - // GetInfo 404s with the bypass, so we use getBasicInfo instead - // that's fine as we only need the streaming data - return await yt.getBasicInfo(id, { - client: 'TV_EMBEDDED', - }); -}; diff --git a/src/plugins/downloader/menu.ts b/src/plugins/downloader/menu.ts index 8838e73ecf..f0c953adb7 100644 --- a/src/plugins/downloader/menu.ts +++ b/src/plugins/downloader/menu.ts @@ -195,6 +195,36 @@ export const onMenu = async ({ } // Else = user pressed cancel }, }, + { + label: t('plugins.downloader.menu.yt-dlp-location-nice'), + click: async () => { + const ytDlpPathValue = typeof config.advanced?.ytDlpPath === 'string' ? config.advanced.ytDlpPath : ''; + const promptRes = await prompt({ + title: t('plugins.downloader.menu.yt-dlp-location-title'), + label: t('plugins.downloader.menu.yt-dlp-location-label'), + value: ytDlpPathValue, + inputAttrs: { + type: 'text', + placeholder: 'C:/yt-dlp.exe or /usr/bin/yt-dlp', + }, + type: 'input', + ...promptOptions(), + }).catch(console.error); + if (typeof promptRes === 'string') { + setConfig({ + advanced: { + ...(config.advanced || {}), + ytDlpPath: promptRes, + }, + }); + dialog.showMessageBox({ + type: 'info', + message: t('plugins.downloader.menu.yt-dlp-location-saved'), + detail: promptRes, + }); + } + }, + }, { label: t('plugins.downloader.menu.presets'), submenu: Object.keys(DefaultPresetList).map((preset) => ({ From 64a039a15a6f902da7f548ccad6f40ca932871c9 Mon Sep 17 00:00:00 2001 From: digitalnomad91 Date: Fri, 19 Sep 2025 00:42:23 -0500 Subject: [PATCH 2/3] feat: add translation strings for yt-dlp downloader - Add new feedback messages for yt-dlp download process - Include file existence check messages - Update error handling text for better user experience --- src/i18n/resources/en.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/resources/en.json b/src/i18n/resources/en.json index 85638a0f6d..43775bfe56 100644 --- a/src/i18n/resources/en.json +++ b/src/i18n/resources/en.json @@ -504,6 +504,7 @@ "downloading-counter": "Downloading {{current}}/{{total}}…", "downloading-playlist": "Downloading playlist \"{{playlistTitle}}\" - {{playlistSize}} songs ({{playlistId}})", "error-while-downloading": "Error downloading \"{{author}} - {{title}}\": {{error}}", + "file-already-exists": "File already exists, skipping download", "folder-already-exists": "The folder {{playlistFolder}} already exists", "getting-playlist-info": "Getting playlist info…", "loading": "Loading…", From 186836dcb53e27d425d71716b4e4ee75e1fcab17 Mon Sep 17 00:00:00 2001 From: digitalnomad91 Date: Fri, 19 Sep 2025 00:43:08 -0500 Subject: [PATCH 3/3] deps: add chalk for enhanced console logging - Add chalk dependency for colored terminal output in yt-dlp downloader - Include TypeScript types for chalk - Update pnpm-lock.yaml with new dependencies --- package.json | 2 + pnpm-lock.yaml | 730 ++++++++++++++++++++++--------------------------- 2 files changed, 328 insertions(+), 404 deletions(-) diff --git a/package.json b/package.json index e209028fd4..82b428328e 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "bgutils-js": "3.2.0", "butterchurn": "3.0.0-beta.5", "butterchurn-presets": "3.0.0-beta.4", + "chalk": "^5.6.2", "color": "5.0.0", "conf": "14.0.0", "custom-electron-prompt": "1.5.8", @@ -142,6 +143,7 @@ "@playwright/test": "1.55.0", "@stylistic/eslint-plugin": "5.3.1", "@total-typescript/ts-reset": "0.6.1", + "@types/chalk": "^2.2.4", "@types/electron-localshortcut": "3.1.3", "@types/howler": "2.2.12", "@types/html-to-text": "9.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60535de66f..539dd6c3d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,7 +38,7 @@ importers: version: 1.0.0 '@electron-toolkit/tsconfig': specifier: 1.0.1 - version: 1.0.1(@types/node@24.3.0) + version: 1.0.1(@types/node@24.4.0) '@electron/remote': specifier: 2.1.3 version: 2.1.3(electron@38.0.0) @@ -105,6 +105,9 @@ importers: butterchurn-presets: specifier: 3.0.0-beta.4 version: 3.0.0-beta.4 + chalk: + specifier: ^5.6.2 + version: 5.6.2 color: specifier: 5.0.0 version: 5.0.0 @@ -265,6 +268,9 @@ importers: '@total-typescript/ts-reset': specifier: 0.6.1 version: 0.6.1 + '@types/chalk': + specifier: ^2.2.4 + version: 2.2.4 '@types/electron-localshortcut': specifier: 3.1.3 version: 3.1.3 @@ -309,7 +315,7 @@ importers: version: 4.0.0 electron-vite: specifier: 4.0.0 - version: 4.0.0(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)) + version: 4.0.0(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)) eslint: specifier: 9.35.0 version: 9.35.0 @@ -354,16 +360,16 @@ importers: version: 6.0.5 vite: specifier: npm:rolldown-vite@7.1.8 - version: rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1) + version: rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1) vite-plugin-inspect: specifier: 11.3.3 - version: 11.3.3(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)) + version: 11.3.3(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)) vite-plugin-resolve: specifier: 2.5.2 version: 2.5.2 vite-plugin-solid: specifier: 2.11.8 - version: 2.11.8(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1))(solid-js@1.9.9) + version: 2.11.8(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1))(solid-js@1.9.9) ws: specifier: 8.18.3 version: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) @@ -373,10 +379,6 @@ packages: 7zip-bin@5.2.0: resolution: {integrity: sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@assemblyscript/loader@0.17.14': resolution: {integrity: sha512-+PVTOfla/0XMLRTQLJFPg4u40XcdTfon6GGea70hBGi8Pd7ZymIXyVUR+vK8wt5Jb4MVKTKPIz43Myyebw5mZA==} @@ -389,12 +391,12 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.0': - resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.3': - resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} engines: {node: '>=6.9.0'} '@babel/generator@7.28.3': @@ -439,12 +441,12 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.3': - resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.3': - resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} engines: {node: '>=6.0.0'} hasBin: true @@ -468,16 +470,16 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.3': - resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.2': - resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} - '@bufbuild/protobuf@2.6.3': - resolution: {integrity: sha512-w/gJKME9mYN7ZoUAmSMAWXk4hkVpxRKvEJCb3dV5g9wwWdxTJJ0ayOJAVcNxtdqaxDyFuC0uz4RSGVacJ030PQ==} + '@bufbuild/protobuf@2.8.0': + resolution: {integrity: sha512-r1/0w5C9dkbcdjyxY8ZHsC5AOWg4Pnzhm2zu7LO4UHSounp2tMm6Y+oioV9zlGbLveE7YaWRDUk48WLxRDgoqg==} '@dehoist/romanize-thai@1.0.0': resolution: {integrity: sha512-6SqD4vyZ48otnypLXMh901CeQetoP5ptYOaIr58N6zDqjjoN0bHszMb5d/6AXJJQf8kIvbmSWBeuDrbAWLajPQ==} @@ -490,8 +492,8 @@ packages: resolution: {integrity: sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==} engines: {node: '>=18'} - '@discordjs/rest@2.5.1': - resolution: {integrity: sha512-Tg9840IneBcbrAjcGaQzHUJWFNq1MMWZjTdjJ0WS/89IffaNKc++iOvffucPxQTF/gviO9+9r8kEPea1X5J2Dw==} + '@discordjs/rest@2.6.0': + resolution: {integrity: sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w==} engines: {node: '>=18'} '@discordjs/util@1.1.1': @@ -560,14 +562,14 @@ packages: engines: {node: '>=14.14'} hasBin: true - '@emnapi/core@1.4.5': - resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} + '@emnapi/core@1.5.0': + resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} - '@emnapi/runtime@1.4.5': - resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} + '@emnapi/runtime@1.5.0': + resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} - '@emnapi/wasi-threads@1.0.4': - resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} @@ -728,8 +730,8 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.8.0': - resolution: {integrity: sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -845,18 +847,14 @@ packages: resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} @@ -1001,6 +999,9 @@ packages: '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1008,8 +1009,8 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.30': - resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@malept/cross-spawn-promise@2.0.0': resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==} @@ -1026,8 +1027,8 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@napi-rs/wasm-runtime@1.0.3': - resolution: {integrity: sha512-rZxtMsLwjdXkMUGC3WwsPwLNVqVqnTJT6MNIB6e+5fhMcSCPP0AOsNWuMQ5mdCq6HNjs/ZeWAEchpqeprqBD2Q==} + '@napi-rs/wasm-runtime@1.0.5': + resolution: {integrity: sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -1253,8 +1254,8 @@ packages: '@ts-morph/common@0.28.0': resolution: {integrity: sha512-4w6X/oFmvXcwux6y6ExfM/xSqMHw20cYwFJH+BlYrtGa6nwY9qGq8GXnUs1sVYeF2o/KT3S8hAH6sKBI3VOkBg==} - '@tybys/wasm-util@0.10.0': - resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1271,6 +1272,10 @@ packages: '@types/cacheable-request@6.0.3': resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + '@types/chalk@2.2.4': + resolution: {integrity: sha512-pb/QoGqtCpH2famSp72qEsXkNzcErlVmiXlQ/ww+5AddD8TmmYS7EWg5T20YiNCAiTgs8pMf2G8SJG5h/ER1ZQ==} + deprecated: This is a stub types definition. chalk provides its own type definitions, so you do not need this installed. + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1307,14 +1312,14 @@ packages: '@types/node@16.9.1': resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==} - '@types/node@20.19.11': - resolution: {integrity: sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==} + '@types/node@20.19.14': + resolution: {integrity: sha512-gqiKWld3YIkmtrrg9zDvg9jfksZCcPywXVN7IauUGhilwGV/yOyeUsvpR796m/Jye0zUzMXPKe8Ct1B79A7N5Q==} - '@types/node@22.17.2': - resolution: {integrity: sha512-gL6z5N9Jm9mhY+U2KXZpteb+09zyffliRkZyZOHODGATyC5B1Jt/7TzuuiLkFsSUMLbS1OLmlj/E+/3KF4Q/4w==} + '@types/node@22.18.3': + resolution: {integrity: sha512-gTVM8js2twdtqM+AE2PdGEe9zGQY4UvmFjan9rZcVb6FGdStfjWoWejdmy4CfWVO9rh5MiYQGZloKAGkJt8lMw==} - '@types/node@24.3.0': - resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} + '@types/node@24.4.0': + resolution: {integrity: sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==} '@types/plist@3.0.5': resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} @@ -1352,32 +1357,16 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.42.0': - resolution: {integrity: sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.43.0': resolution: {integrity: sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.42.0': - resolution: {integrity: sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.43.0': resolution: {integrity: sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.42.0': - resolution: {integrity: sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.43.0': resolution: {integrity: sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1391,33 +1380,16 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.42.0': - resolution: {integrity: sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.43.0': resolution: {integrity: sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.42.0': - resolution: {integrity: sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.43.0': resolution: {integrity: sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.42.0': - resolution: {integrity: sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.43.0': resolution: {integrity: sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1425,10 +1397,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.42.0': - resolution: {integrity: sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.43.0': resolution: {integrity: sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1609,16 +1577,16 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} ansis@4.1.0: @@ -1734,6 +1702,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.8.3: + resolution: {integrity: sha512-mcE+Wr2CAhHNWxXN/DdTI+n4gsPc5QpXpWnyCQWiQYIYZX+ZMJ8juXZgjRa/0/YPJo/NSsgW15/YgmI4nbysYw==} + hasBin: true + bgutils-js@3.2.0: resolution: {integrity: sha512-CacO15JvxbclbLeCAAm9DETGlLuisRGWpPigoRvNsccSCPEC4pwYwA2g2x/pv7Om/sk79d4ib35V5HHmxPBpDg==} @@ -1770,8 +1742,8 @@ packages: browser-extension-url-match@1.2.0: resolution: {integrity: sha512-+O/t71m1opNU5KG/bJkeNLvXLp0OxlFekjdR8w6waUOyWhkL6+bnQ6dCDoJxc6YF6ZQM0r64ag/d9K4m05ULsg==} - browserslist@4.25.2: - resolution: {integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==} + browserslist@4.26.0: + resolution: {integrity: sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1860,8 +1832,8 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} - caniuse-lite@1.0.30001735: - resolution: {integrity: sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==} + caniuse-lite@1.0.30001741: + resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==} chalk-template@0.4.0: resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} @@ -1875,6 +1847,10 @@ packages: resolution: {integrity: sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -1936,19 +1912,19 @@ packages: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-convert@3.1.0: - resolution: {integrity: sha512-TVoqAq8ZDIpK5lsQY874DDnu65CSsc9vzq0wLpNQ6UMBq81GSZocVazPiBbYGzngzBOIRahpkTzCLVe2at4MfA==} + color-convert@3.1.2: + resolution: {integrity: sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==} engines: {node: '>=14.6'} color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-name@2.0.0: - resolution: {integrity: sha512-SbtvAMWvASO5TE2QP07jHBMXKafgdZz8Vrsrn96fiL+O92/FN/PLARzUW5sKt013fjAprK2d2iCn2hk2Xb5oow==} + color-name@2.0.2: + resolution: {integrity: sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==} engines: {node: '>=12.20'} - color-string@2.0.1: - resolution: {integrity: sha512-5z9FbYTZPAo8iKsNEqRNv+OlpBbDcoE+SY9GjLfDUHEfcNNV7tS9eSAlFHEaub/r5tBL9LtskAeq1l9SaoZ5tQ==} + color-string@2.1.2: + resolution: {integrity: sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==} engines: {node: '>=18'} color@5.0.0: @@ -1975,8 +1951,8 @@ packages: resolution: {integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==} engines: {node: '>=0.10.0'} - component-register@0.8.7: - resolution: {integrity: sha512-clPS/o1RNfJw7L1/w4q+nkj6l7JV32kFHCx6vW5nSPOEly4B9olMeADNilEgpLV/DdeS7y8JXhHKx9YvSj8vqQ==} + component-register@0.8.8: + resolution: {integrity: sha512-djhwcxjY+X9dacaYUEOkOm7tda8uOEDiMDigWysu3xv54M8o6XDlsjR1qt5Y8QLGiKg51fqXFIR2HUTmt9ys0Q==} compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} @@ -2075,8 +2051,8 @@ packages: supports-color: optional: true - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -2264,8 +2240,8 @@ packages: resolution: {integrity: sha512-oL8bRy7pVCLpwhmXy05Rh/L6O93+k9t6dqSw0+MckIc3OmCTZm6Mp04Q4f/J0rtu84Ky6ywkR8ivtGOmrq+16w==} engines: {node: '>=20'} - electron-to-chromium@1.5.203: - resolution: {integrity: sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==} + electron-to-chromium@1.5.218: + resolution: {integrity: sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==} electron-unhandled@5.0.0: resolution: {integrity: sha512-spH7quQUrNWgTp1rJNa0sCPTPHwKywctnHLVbrQxpjxojQLhGxXHMdETBkag0No8x9Jwo/c6r2T/nfeoG+4Cxw==} @@ -2577,8 +2553,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.0.6: - resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -3154,8 +3130,8 @@ packages: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} engines: {node: '>= 8.0.0'} - isbinaryfile@5.0.4: - resolution: {integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==} + isbinaryfile@5.0.6: + resolution: {integrity: sha512-I+NmIfBHUl+r2wcDd6JwE9yWje/PIVY/R5/CmV8dXLZd5K+L9X2klAOwfAHNnondLXkbHyTAleQAWonpTJBTtw==} engines: {node: '>= 18.0.0'} isexe@2.0.0: @@ -3380,8 +3356,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.1.0: - resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + lru-cache@11.2.1: + resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -3398,8 +3374,8 @@ packages: magic-bytes.js@1.12.1: resolution: {integrity: sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA==} - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} make-fetch-happen@10.2.1: resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} @@ -3596,8 +3572,8 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - node-abi@3.75.0: - resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} + node-abi@3.77.0: + resolution: {integrity: sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==} engines: {node: '>=10'} node-addon-api@1.7.2: @@ -3630,8 +3606,8 @@ packages: node-id3@0.2.9: resolution: {integrity: sha512-dSxhuxrkkGVRgUhDHFxdY0pilzOREcodO01HcZWfaRkCaPWGmo0dOgD8ygyL6ln4Iv4cmfRxAWn1WD9bIB9Bhw==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.21: + resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} nopt@6.0.0: resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} @@ -4092,8 +4068,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - run-applescript@7.0.0: - resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} run-parallel@1.2.0: @@ -4156,8 +4132,8 @@ packages: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} - seroval-plugins@1.3.2: - resolution: {integrity: sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==} + seroval-plugins@1.3.3: + resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} engines: {node: '>=10'} peerDependencies: seroval: ^1.0 @@ -4233,8 +4209,8 @@ packages: version: 2.5.9 engines: {node: '>=18.0.0', npm: '>=7.0.0'} - sirv@3.0.1: - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} slash@5.1.0: @@ -4362,8 +4338,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-bom@3.0.0: @@ -4438,10 +4414,6 @@ packages: tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -4451,11 +4423,11 @@ packages: engines: {node: '>= 12.10.0', npm: '>= 6.12.0', yarn: '>= 1.20.0'} hasBin: true - tldts-core@7.0.12: - resolution: {integrity: sha512-3K76aXywJFduGRsOYoY5JzINLs/WMlOkeDwPL+8OCPq2Rh39gkSDtWAxdJQlWjpun/xF/LHf29yqCi6VC/rHDA==} + tldts-core@7.0.14: + resolution: {integrity: sha512-viZGNK6+NdluOJWwTO9olaugx0bkKhscIdriQQ+lNNhwitIKvb+SvhbYgnCz6j9p7dX3cJntt4agQAKMXLjJ5g==} - tldts-experimental@7.0.12: - resolution: {integrity: sha512-riDDp/JrJJuh/2GV1Tgg6OnOwcWlgleBKi4/xkhd0DW7tvvmNjmWXCh2nj7Z2G1k9S/AGN4RiiPRbvhGJUCxeg==} + tldts-experimental@7.0.14: + resolution: {integrity: sha512-sZ90eUcfG6Wm4dUCnloL6fuWyV1T+QmeluFYhFApMpb9hnd+/BeBBl6lt7fuf0ACEXmUh6QfyrwCNFC4HNzTnA==} tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} @@ -4538,8 +4510,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - uint8array-extras@1.4.1: - resolution: {integrity: sha512-+NWHrac9dvilNgme+gP4YrBSumsaMZP0fNBtXXFIf33RLLKEcBUKaQZ7ULUbS0sBfcjxIZ4V96OTRkCbM7hxpw==} + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} unbox-primitive@1.1.0: @@ -4549,8 +4521,8 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.10.0: - resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + undici-types@7.11.0: + resolution: {integrity: sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==} undici@6.21.3: resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} @@ -4848,11 +4820,6 @@ snapshots: 7zip-bin@5.2.0: {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 - '@assemblyscript/loader@0.17.14': {} '@asteasolutions/zod-to-openapi@8.1.0(zod@4.1.5)': @@ -4866,22 +4833,22 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.0': {} + '@babel/compat-data@7.28.4': {} - '@babel/core@7.28.3': + '@babel/core@7.28.4': dependencies: - '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 '@babel/generator': 7.28.3 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) - '@babel/helpers': 7.28.3 - '@babel/parser': 7.28.3 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -4890,17 +4857,17 @@ snapshots: '@babel/generator@7.28.3': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.0 + '@babel/compat-data': 7.28.4 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.2 + browserslist: 4.26.0 lru-cache: 5.1.1 semver: 6.3.1 @@ -4908,21 +4875,21 @@ snapshots: '@babel/helper-module-imports@7.18.6': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -4934,23 +4901,23 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.3': + '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 - '@babel/parser@7.28.3': + '@babel/parser@7.28.4': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/runtime@7.28.4': {} @@ -4958,27 +4925,27 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 - '@babel/traverse@7.28.3': + '@babel/traverse@7.28.4': dependencies: '@babel/code-frame': 7.27.1 '@babel/generator': 7.28.3 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.3 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/types': 7.28.2 - debug: 4.4.1 + '@babel/types': 7.28.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.28.2': + '@babel/types@7.28.4': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@bufbuild/protobuf@2.6.3': {} + '@bufbuild/protobuf@2.8.0': {} '@dehoist/romanize-thai@1.0.0': {} @@ -4989,7 +4956,7 @@ snapshots: '@discordjs/collection@2.1.1': {} - '@discordjs/rest@2.5.1': + '@discordjs/rest@2.6.0': dependencies: '@discordjs/collection': 2.1.1 '@discordjs/util': 1.1.1 @@ -5003,9 +4970,9 @@ snapshots: '@discordjs/util@1.1.1': {} - '@electron-toolkit/tsconfig@1.0.1(@types/node@24.3.0)': + '@electron-toolkit/tsconfig@1.0.1(@types/node@24.4.0)': dependencies: - '@types/node': 24.3.0 + '@types/node': 24.4.0 '@electron/asar@3.2.18': dependencies: @@ -5033,7 +5000,7 @@ snapshots: '@electron/get@2.0.3': dependencies: - debug: 4.4.1 + debug: 4.4.3 env-paths: 2.2.1 fs-extra: 8.1.0 got: 11.8.6 @@ -5063,7 +5030,7 @@ snapshots: '@electron/notarize@2.5.0': dependencies: - debug: 4.4.1 + debug: 4.4.3 fs-extra: 9.1.0 promise-retry: 2.0.1 transitivePeerDependencies: @@ -5072,7 +5039,7 @@ snapshots: '@electron/osx-sign@1.3.1': dependencies: compare-version: 0.1.2 - debug: 4.4.1 + debug: 4.4.3 fs-extra: 10.1.0 isbinaryfile: 4.0.10 minimist: 1.2.8 @@ -5085,11 +5052,11 @@ snapshots: '@electron/node-gyp': https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2 '@malept/cross-spawn-promise': 2.0.0 chalk: 4.1.2 - debug: 4.4.1 + debug: 4.4.3 detect-libc: 2.0.4 fs-extra: 10.1.0 got: 11.8.6 - node-abi: 3.75.0 + node-abi: 3.77.0 node-api-version: 0.2.1 ora: 5.4.1 read-binary-file-arch: 1.0.6 @@ -5108,7 +5075,7 @@ snapshots: dependencies: '@electron/asar': 4.0.1 '@malept/cross-spawn-promise': 2.0.0 - debug: 4.4.1 + debug: 4.4.3 dir-compare: 4.2.0 minimatch: 9.0.5 plist: 3.1.0 @@ -5118,7 +5085,7 @@ snapshots: '@electron/windows-sign@1.2.2': dependencies: cross-dirname: 0.1.0 - debug: 4.4.1 + debug: 4.4.3 fs-extra: 11.3.1 minimist: 1.2.8 postject: 1.0.0-alpha.6 @@ -5126,18 +5093,18 @@ snapshots: - supports-color optional: true - '@emnapi/core@1.4.5': + '@emnapi/core@1.5.0': dependencies: - '@emnapi/wasi-threads': 1.0.4 + '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.4.5': + '@emnapi/runtime@1.5.0': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.0.4': + '@emnapi/wasi-threads@1.1.0': dependencies: tslib: 2.8.1 optional: true @@ -5222,7 +5189,7 @@ snapshots: '@esbuild/win32-x64@0.25.9': optional: true - '@eslint-community/eslint-utils@4.8.0(eslint@9.35.0)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.35.0)': dependencies: eslint: 9.35.0 eslint-visitor-keys: 3.4.3 @@ -5232,7 +5199,7 @@ snapshots: '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.1 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -5246,7 +5213,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.1 + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -5303,7 +5270,7 @@ snapshots: '@ghostery/adblocker': 2.11.6 '@ghostery/adblocker-electron-preload': 2.11.6(electron@38.0.0) electron: 38.0.0 - tldts-experimental: 7.0.12 + tldts-experimental: 7.0.14 '@ghostery/adblocker-extended-selectors@2.11.6': {} @@ -5315,11 +5282,11 @@ snapshots: '@remusao/guess-url-type': 2.1.0 '@remusao/small': 2.1.0 '@remusao/smaz': 2.2.0 - tldts-experimental: 7.0.12 + tldts-experimental: 7.0.14 '@ghostery/url-parser@1.3.0': dependencies: - tldts-experimental: 7.0.12 + tldts-experimental: 7.0.14 '@hono/node-server@1.19.1(hono@4.9.6)': dependencies: @@ -5353,15 +5320,13 @@ snapshots: '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.6': + '@humanfs/node@0.16.7': dependencies: '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.3': {} '@isaacs/balanced-match@4.0.1': {} @@ -5374,7 +5339,7 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -5595,13 +5560,18 @@ snapshots: '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.30': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 @@ -5612,7 +5582,7 @@ snapshots: '@malept/flatpak-bundler@0.4.0(patch_hash=c787371eeb2af011ea934e8818a0dad6d7dcb2df31bbb1686babc7231af0183c)': dependencies: - debug: 4.4.1 + debug: 4.4.3 fs-extra: 9.1.0 lodash: 4.17.21 tmp-promise: 3.0.3 @@ -5623,16 +5593,16 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.4.5 - '@emnapi/runtime': 1.4.5 - '@tybys/wasm-util': 0.10.0 + '@emnapi/core': 1.5.0 + '@emnapi/runtime': 1.5.0 + '@tybys/wasm-util': 0.10.1 optional: true - '@napi-rs/wasm-runtime@1.0.3': + '@napi-rs/wasm-runtime@1.0.5': dependencies: - '@emnapi/core': 1.4.5 - '@emnapi/runtime': 1.4.5 - '@tybys/wasm-util': 0.10.0 + '@emnapi/core': 1.5.0 + '@emnapi/runtime': 1.5.0 + '@tybys/wasm-util': 0.10.1 optional: true '@nodelib/fs.scandir@2.1.5': @@ -5737,7 +5707,7 @@ snapshots: '@rolldown/binding-wasm32-wasi@1.0.0-beta.36': dependencies: - '@napi-rs/wasm-runtime': 1.0.3 + '@napi-rs/wasm-runtime': 1.0.5 optional: true '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.36': @@ -5783,8 +5753,8 @@ snapshots: '@stylistic/eslint-plugin@5.3.1(eslint@9.35.0)': dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0) - '@typescript-eslint/types': 8.42.0 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0) + '@typescript-eslint/types': 8.43.0 eslint: 9.35.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 @@ -5805,41 +5775,45 @@ snapshots: dependencies: minimatch: 10.0.3 path-browserify: 1.0.1 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 - '@tybys/wasm-util@0.10.0': + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 optional: true '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 24.3.0 + '@types/node': 22.18.3 '@types/responselike': 1.0.3 + '@types/chalk@2.2.4': + dependencies: + chalk: 5.6.2 + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -5854,7 +5828,7 @@ snapshots: '@types/fs-extra@9.0.13': dependencies: - '@types/node': 24.3.0 + '@types/node': 24.4.0 '@types/howler@2.2.12': {} @@ -5868,33 +5842,33 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 24.3.0 + '@types/node': 22.18.3 '@types/ms@2.1.0': {} '@types/node@16.9.1': {} - '@types/node@20.19.11': + '@types/node@20.19.14': dependencies: undici-types: 6.21.0 - '@types/node@22.17.2': + '@types/node@22.18.3': dependencies: undici-types: 6.21.0 - '@types/node@24.3.0': + '@types/node@24.4.0': dependencies: - undici-types: 7.10.0 + undici-types: 7.11.0 '@types/plist@3.0.5': dependencies: - '@types/node': 24.3.0 + '@types/node': 24.4.0 xmlbuilder: 15.1.1 optional: true '@types/responselike@1.0.3': dependencies: - '@types/node': 24.3.0 + '@types/node': 22.18.3 '@types/semver@7.7.1': {} @@ -5907,7 +5881,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.3.0 + '@types/node': 22.18.3 optional: true '@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(typescript@5.9.2)': @@ -5933,44 +5907,26 @@ snapshots: '@typescript-eslint/types': 8.43.0 '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) '@typescript-eslint/visitor-keys': 8.43.0 - debug: 4.4.1 + debug: 4.4.3 eslint: 9.35.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.42.0(typescript@5.9.2)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.9.2) - '@typescript-eslint/types': 8.42.0 - debug: 4.4.1 - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/project-service@8.43.0(typescript@5.9.2)': dependencies: '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.2) '@typescript-eslint/types': 8.43.0 - debug: 4.4.1 + debug: 4.4.3 typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.42.0': - dependencies: - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/visitor-keys': 8.42.0 - '@typescript-eslint/scope-manager@8.43.0': dependencies: '@typescript-eslint/types': 8.43.0 '@typescript-eslint/visitor-keys': 8.43.0 - '@typescript-eslint/tsconfig-utils@8.42.0(typescript@5.9.2)': - dependencies: - typescript: 5.9.2 - '@typescript-eslint/tsconfig-utils@8.43.0(typescript@5.9.2)': dependencies: typescript: 5.9.2 @@ -5980,40 +5936,22 @@ snapshots: '@typescript-eslint/types': 8.43.0 '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) '@typescript-eslint/utils': 8.43.0(eslint@9.35.0)(typescript@5.9.2) - debug: 4.4.1 + debug: 4.4.3 eslint: 9.35.0 ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.42.0': {} - '@typescript-eslint/types@8.43.0': {} - '@typescript-eslint/typescript-estree@8.42.0(typescript@5.9.2)': - dependencies: - '@typescript-eslint/project-service': 8.42.0(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.9.2) - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/visitor-keys': 8.42.0 - debug: 4.4.1 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@8.43.0(typescript@5.9.2)': dependencies: '@typescript-eslint/project-service': 8.43.0(typescript@5.9.2) '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.2) '@typescript-eslint/types': 8.43.0 '@typescript-eslint/visitor-keys': 8.43.0 - debug: 4.4.1 + debug: 4.4.3 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -6023,20 +5961,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.42.0(eslint@9.35.0)(typescript@5.9.2)': - dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0) - '@typescript-eslint/scope-manager': 8.42.0 - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) - eslint: 9.35.0 - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@8.43.0(eslint@9.35.0)(typescript@5.9.2)': dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0) '@typescript-eslint/scope-manager': 8.43.0 '@typescript-eslint/types': 8.43.0 '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) @@ -6045,11 +5972,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.42.0': - dependencies: - '@typescript-eslint/types': 8.42.0 - eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.43.0': dependencies: '@typescript-eslint/types': 8.43.0 @@ -6118,7 +6040,7 @@ snapshots: '@xhayper/discord-rpc@1.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)': dependencies: - '@discordjs/rest': 2.5.1 + '@discordjs/rest': 2.6.0 '@vladfrangu/async_event_emitter': 2.4.6 discord-api-types: 0.38.23 ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) @@ -6146,7 +6068,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -6186,7 +6108,7 @@ snapshots: ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.0.6 + fast-uri: 3.1.0 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -6196,13 +6118,13 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} ansis@4.1.0: {} @@ -6226,7 +6148,7 @@ snapshots: builder-util-runtime: 9.3.1 chromium-pickle-js: 0.2.0 config-file-ts: 0.2.8-rc1 - debug: 4.4.1 + debug: 4.4.3 dmg-builder: 26.0.12(electron-builder-squirrel-windows@26.0.12) dotenv: 16.6.1 dotenv-expand: 11.0.7 @@ -6236,7 +6158,7 @@ snapshots: fs-extra: 10.1.0 hosted-git-info: 4.1.0 is-ci: 3.0.1 - isbinaryfile: 5.0.4 + isbinaryfile: 5.0.6 js-yaml: 4.1.0 json5: 2.2.3 lazy-val: 1.0.5 @@ -6342,20 +6264,20 @@ snapshots: await-to-js@3.0.0: {} - babel-plugin-jsx-dom-expressions@0.40.1(@babel/core@7.28.3): + babel-plugin-jsx-dom-expressions@0.40.1(@babel/core@7.28.4): dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.18.6 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.3) - '@babel/types': 7.28.2 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/types': 7.28.4 html-entities: 2.3.3 parse5: 7.3.0 validate-html-nesting: 1.2.3 - babel-preset-solid@1.9.9(@babel/core@7.28.3)(solid-js@1.9.9): + babel-preset-solid@1.9.9(@babel/core@7.28.4)(solid-js@1.9.9): dependencies: - '@babel/core': 7.28.3 - babel-plugin-jsx-dom-expressions: 0.40.1(@babel/core@7.28.3) + '@babel/core': 7.28.4 + babel-plugin-jsx-dom-expressions: 0.40.1(@babel/core@7.28.4) optionalDependencies: solid-js: 1.9.9 @@ -6363,6 +6285,8 @@ snapshots: base64-js@1.5.1: {} + baseline-browser-mapping@2.8.3: {} + bgutils-js@3.2.0: {} birpc@2.5.0: {} @@ -6384,7 +6308,7 @@ snapshots: dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 - chalk: 5.0.1 + chalk: 5.6.2 cli-boxes: 3.0.0 string-width: 5.1.2 type-fest: 2.19.0 @@ -6408,12 +6332,13 @@ snapshots: dependencies: fancy-regex: 0.5.4 - browserslist@4.25.2: + browserslist@4.26.0: dependencies: - caniuse-lite: 1.0.30001735 - electron-to-chromium: 1.5.203 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.2) + baseline-browser-mapping: 2.8.3 + caniuse-lite: 1.0.30001741 + electron-to-chromium: 1.5.218 + node-releases: 2.0.21 + update-browserslist-db: 1.1.3(browserslist@4.26.0) buffer-crc32@0.2.13: {} @@ -6435,7 +6360,7 @@ snapshots: builder-util-runtime@9.3.1: dependencies: - debug: 4.4.1 + debug: 4.4.3 sax: 1.4.1 transitivePeerDependencies: - supports-color @@ -6448,7 +6373,7 @@ snapshots: builder-util-runtime: 9.3.1 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.3 fs-extra: 10.1.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -6466,7 +6391,7 @@ snapshots: bundle-name@4.1.0: dependencies: - run-applescript: 7.0.0 + run-applescript: 7.1.0 butterchurn-presets@3.0.0-beta.4: dependencies: @@ -6555,7 +6480,7 @@ snapshots: camelcase@7.0.1: {} - caniuse-lite@1.0.30001735: {} + caniuse-lite@1.0.30001741: {} chalk-template@0.4.0: dependencies: @@ -6568,6 +6493,8 @@ snapshots: chalk@5.0.1: {} + chalk@5.6.2: {} + chownr@2.0.0: {} chownr@3.0.0: {} @@ -6620,22 +6547,22 @@ snapshots: dependencies: color-name: 1.1.4 - color-convert@3.1.0: + color-convert@3.1.2: dependencies: - color-name: 2.0.0 + color-name: 2.0.2 color-name@1.1.4: {} - color-name@2.0.0: {} + color-name@2.0.2: {} - color-string@2.0.1: + color-string@2.1.2: dependencies: - color-name: 2.0.0 + color-name: 2.0.2 color@5.0.0: dependencies: - color-convert: 3.1.0 - color-string: 2.0.1 + color-convert: 3.1.2 + color-string: 2.1.2 combined-stream@1.0.8: dependencies: @@ -6650,7 +6577,7 @@ snapshots: compare-version@0.1.2: {} - component-register@0.8.7: {} + component-register@0.8.8: {} compressible@2.0.18: dependencies: @@ -6680,7 +6607,7 @@ snapshots: env-paths: 3.0.0 json-schema-typed: 8.0.1 semver: 7.7.2 - uint8array-extras: 1.4.1 + uint8array-extras: 1.5.0 config-file-ts@0.2.8-rc1: dependencies: @@ -6763,7 +6690,7 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.1: + debug@4.4.3: dependencies: ms: 2.1.3 @@ -6987,7 +6914,7 @@ snapshots: electron-localshortcut@3.2.1: dependencies: - debug: 4.4.1 + debug: 4.4.3 electron-is-accelerator: 0.1.2 keyboardevent-from-electron-accelerator: 2.0.0 keyboardevents-areequal: 0.2.2 @@ -7012,7 +6939,7 @@ snapshots: conf: 14.0.0 type-fest: 4.41.0 - electron-to-chromium@1.5.203: {} + electron-to-chromium@1.5.218: {} electron-unhandled@5.0.0: dependencies: @@ -7035,22 +6962,22 @@ snapshots: transitivePeerDependencies: - supports-color - electron-vite@4.0.0(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)): + electron-vite@4.0.0(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)): dependencies: - '@babel/core': 7.28.3 - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.28.4 + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.4) cac: 6.7.14 esbuild: 0.25.9 - magic-string: 0.30.17 + magic-string: 0.30.19 picocolors: 1.1.1 - vite: rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1) + vite: rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1) transitivePeerDependencies: - supports-color electron-winstaller@5.4.0: dependencies: '@electron/asar': 3.4.1 - debug: 4.4.1 + debug: 4.4.3 fs-extra: 7.0.1 lodash: 4.17.21 temp: 0.9.4 @@ -7062,7 +6989,7 @@ snapshots: electron@38.0.0: dependencies: '@electron/get': 2.0.3 - '@types/node': 22.17.2 + '@types/node': 22.18.3 extract-zip: 2.0.1 transitivePeerDependencies: - supports-color @@ -7255,13 +7182,13 @@ snapshots: eslint-import-resolver-typescript@4.4.4(eslint-plugin-import@2.32.0)(eslint@9.35.0): dependencies: - debug: 4.4.1 + debug: 4.4.3 eslint: 9.35.0 eslint-import-context: 0.1.9(unrs-resolver@1.11.1) get-tsconfig: 4.10.1 is-bun-module: 2.0.0 stable-hash-x: 0.2.0 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0)(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0) @@ -7319,7 +7246,7 @@ snapshots: eslint-plugin-solid@0.14.5(eslint@9.35.0)(typescript@5.9.2): dependencies: - '@typescript-eslint/utils': 8.42.0(eslint@9.35.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0)(typescript@5.9.2) eslint: 9.35.0 estraverse: 5.3.0 is-html: 2.0.0 @@ -7341,7 +7268,7 @@ snapshots: eslint@9.35.0: dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@9.35.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 '@eslint/config-helpers': 0.3.1 @@ -7349,7 +7276,7 @@ snapshots: '@eslint/eslintrc': 3.3.1 '@eslint/js': 9.35.0 '@eslint/plugin-kit': 0.3.5 - '@humanfs/node': 0.16.6 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 @@ -7357,7 +7284,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -7431,7 +7358,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.1 + debug: 4.4.3 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -7464,7 +7391,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.0.6: {} + fast-uri@3.1.0: {} fastq@1.19.1: dependencies: @@ -7745,7 +7672,7 @@ snapshots: happy-dom@18.0.1: dependencies: - '@types/node': 20.19.11 + '@types/node': 20.19.14 '@types/whatwg-mimetype': 3.0.2 whatwg-mimetype: 3.0.0 @@ -7806,14 +7733,14 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -7825,14 +7752,14 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -8070,7 +7997,7 @@ snapshots: isbinaryfile@4.0.10: {} - isbinaryfile@5.0.4: {} + isbinaryfile@5.0.6: {} isexe@2.0.0: {} @@ -8287,7 +8214,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.1.0: {} + lru-cache@11.2.1: {} lru-cache@5.1.1: dependencies: @@ -8301,7 +8228,7 @@ snapshots: magic-bytes.js@1.12.1: {} - magic-string@0.30.17: + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -8488,7 +8415,7 @@ snapshots: negotiator@1.0.0: {} - node-abi@3.75.0: + node-abi@3.77.0: dependencies: semver: 7.7.2 @@ -8519,7 +8446,7 @@ snapshots: proc-log: 5.0.0 semver: 7.7.2 tar: 7.4.3 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 which: 5.0.0 transitivePeerDependencies: - supports-color @@ -8533,7 +8460,7 @@ snapshots: dependencies: iconv-lite: 0.6.2 - node-releases@2.0.19: {} + node-releases@2.0.21: {} nopt@6.0.0: dependencies: @@ -8706,7 +8633,7 @@ snapshots: path-scurry@2.0.0: dependencies: - lru-cache: 11.1.0 + lru-cache: 11.2.1 minipass: 7.1.2 path-to-regexp@3.3.0: {} @@ -8826,7 +8753,7 @@ snapshots: read-binary-file-arch@1.0.6: dependencies: - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -8942,7 +8869,7 @@ snapshots: sprintf-js: 1.1.3 optional: true - rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1): + rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1): dependencies: fdir: 6.5.0(picomatch@4.0.3) lightningcss: 1.30.1 @@ -8951,7 +8878,7 @@ snapshots: rolldown: 1.0.0-beta.36 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.4.0 esbuild: 0.25.9 fsevents: 2.3.3 yaml: 2.8.1 @@ -8978,7 +8905,7 @@ snapshots: '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.36 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.36 - run-applescript@7.0.0: {} + run-applescript@7.1.0: {} run-parallel@1.2.0: dependencies: @@ -9039,7 +8966,7 @@ snapshots: type-fest: 0.13.1 optional: true - seroval-plugins@1.3.2(seroval@1.3.2): + seroval-plugins@1.3.3(seroval@1.3.2): dependencies: seroval: 1.3.2 @@ -9141,7 +9068,7 @@ snapshots: simple-youtube-age-restriction-bypass@https://codeload.github.com/organization/Simple-YouTube-Age-Restriction-Bypass/tar.gz/4e2db89ccb2fb880c5110add9ff3f1dfb78d0ff6: {} - sirv@3.0.1: + sirv@3.0.2: dependencies: '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 @@ -9161,7 +9088,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 socks: 2.8.7 transitivePeerDependencies: - supports-color @@ -9169,7 +9096,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 socks: 2.8.7 transitivePeerDependencies: - supports-color @@ -9181,7 +9108,7 @@ snapshots: solid-element@1.9.1(solid-js@1.9.9): dependencies: - component-register: 0.8.7 + component-register: 0.8.8 solid-js: 1.9.9 solid-floating-ui@0.3.1(@floating-ui/dom@1.7.4)(solid-js@1.9.9): @@ -9193,13 +9120,13 @@ snapshots: dependencies: csstype: 3.1.3 seroval: 1.3.2 - seroval-plugins: 1.3.2(seroval@1.3.2) + seroval-plugins: 1.3.3(seroval@1.3.2) solid-refresh@0.6.3(solid-js@1.9.9): dependencies: '@babel/generator': 7.28.3 '@babel/helper-module-imports': 7.27.1 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 solid-js: 1.9.9 transitivePeerDependencies: - supports-color @@ -9264,7 +9191,7 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 string.prototype.trim@1.2.10: dependencies: @@ -9301,9 +9228,9 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 strip-bom@3.0.0: {} @@ -9326,7 +9253,7 @@ snapshots: sumchecker@3.0.1: dependencies: - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -9380,11 +9307,6 @@ snapshots: tinycolor2@1.6.0: {} - tinyglobby@0.2.14: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -9392,11 +9314,11 @@ snapshots: tinyld@1.3.4: {} - tldts-core@7.0.12: {} + tldts-core@7.0.14: {} - tldts-experimental@7.0.12: + tldts-experimental@7.0.14: dependencies: - tldts-core: 7.0.12 + tldts-core: 7.0.14 tmp-promise@3.0.3: dependencies: @@ -9494,7 +9416,7 @@ snapshots: typescript@5.9.2: {} - uint8array-extras@1.4.1: {} + uint8array-extras@1.5.0: {} unbox-primitive@1.1.0: dependencies: @@ -9505,7 +9427,7 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.10.0: {} + undici-types@7.11.0: {} undici@6.21.3: {} @@ -9566,9 +9488,9 @@ snapshots: mkdirp: 0.5.6 yaku: 0.16.7 - update-browserslist-db@1.1.3(browserslist@4.25.2): + update-browserslist-db@1.1.3(browserslist@4.26.0): dependencies: - browserslist: 4.25.2 + browserslist: 4.26.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -9610,28 +9532,28 @@ snapshots: optionalDependencies: solid-js: 1.9.9 - vite-dev-rpc@1.1.0(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)): + vite-dev-rpc@1.1.0(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)): dependencies: birpc: 2.5.0 - vite: rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1) - vite-hot-client: 2.1.0(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)) + vite: rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1) + vite-hot-client: 2.1.0(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)) - vite-hot-client@2.1.0(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)): + vite-hot-client@2.1.0(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)): dependencies: - vite: rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1) + vite: rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1) - vite-plugin-inspect@11.3.3(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)): + vite-plugin-inspect@11.3.3(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)): dependencies: ansis: 4.1.0 - debug: 4.4.1 + debug: 4.4.3 error-stack-parser-es: 1.0.5 ohash: 2.0.11 open: 10.2.0 perfect-debounce: 2.0.0 - sirv: 3.0.1 + sirv: 3.0.2 unplugin-utils: 0.3.0 - vite: rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1) - vite-dev-rpc: 1.1.0(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)) + vite: rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1) + vite-dev-rpc: 1.1.0(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)) transitivePeerDependencies: - supports-color @@ -9639,22 +9561,22 @@ snapshots: dependencies: lib-esm: 0.4.2 - vite-plugin-solid@2.11.8(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1))(solid-js@1.9.9): + vite-plugin-solid@2.11.8(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1))(solid-js@1.9.9): dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@types/babel__core': 7.20.5 - babel-preset-solid: 1.9.9(@babel/core@7.28.3)(solid-js@1.9.9) + babel-preset-solid: 1.9.9(@babel/core@7.28.4)(solid-js@1.9.9) merge-anything: 5.1.7 solid-js: 1.9.9 solid-refresh: 0.6.3(solid-js@1.9.9) - vite: rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1) - vitefu: 1.1.1(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)) + vite: rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1) + vitefu: 1.1.1(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)) transitivePeerDependencies: - supports-color - vitefu@1.1.1(rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1)): + vitefu@1.1.1(rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1)): optionalDependencies: - vite: rolldown-vite@7.1.8(@types/node@24.3.0)(esbuild@0.25.9)(yaml@2.8.1) + vite: rolldown-vite@7.1.8(@types/node@24.4.0)(esbuild@0.25.9)(yaml@2.8.1) vudio@2.1.1(patch_hash=0e06c2ed11c02bdc490c209fa80070e98517c2735c641f5738b6e15d7dc1959d): {} @@ -9735,9 +9657,9 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 wrappy@1.0.2: {} @@ -9798,7 +9720,7 @@ snapshots: youtubei.js@15.0.1: dependencies: - '@bufbuild/protobuf': 2.6.3 + '@bufbuild/protobuf': 2.8.0 jintr: 3.3.1 undici: 6.21.3