diff --git a/.serena/cache/typescript/document_symbols_cache_v23-06-25.pkl b/.serena/cache/typescript/document_symbols_cache_v23-06-25.pkl index 6dc5422..c63c390 100644 Binary files a/.serena/cache/typescript/document_symbols_cache_v23-06-25.pkl and b/.serena/cache/typescript/document_symbols_cache_v23-06-25.pkl differ diff --git a/YOUTUBE_SETUP.md b/YOUTUBE_SETUP.md deleted file mode 100644 index 598048d..0000000 --- a/YOUTUBE_SETUP.md +++ /dev/null @@ -1,70 +0,0 @@ -# YouTube Authentication Setup - -Чтобы обойти блокировку YouTube "Sign in to confirm you're not a bot", нужно использовать cookies из вашего авторизованного браузера. - -## Шаг 1: Получение cookies - -1. Откройте Chrome/Firefox и зайдите на [youtube.com](https://youtube.com) -2. Убедитесь что вы авторизованы в своем аккаунте -3. Нажмите F12 чтобы открыть Developer Tools -4. Перейдите на вкладку **Application** (Chrome) или **Storage** (Firefox) -5. В левом меню найдите **Cookies** → **https://www.youtube.com** -6. Найдите и скопируйте значения следующих cookies: - -### Обязательные cookies: -- `__Secure-1PSID` -- `__Secure-3PSID` -- `__Secure-1PAPISID` -- `__Secure-3PAPISID` - -### Дополнительные (рекомендуемые): -- `VISITOR_INFO1_LIVE` -- `YSC` - -## Шаг 2: Настройка файла - -1. Откройте файл `youtube-cookies.json` в корне проекта -2. Замените `your_*_value_here` на реальные значения из браузера: - -```json -{ - "comment": "Replace these values with your actual YouTube cookies from browser", - "__Secure-1PSID": "СКОПИРОВАННОЕ_ЗНАЧЕНИЕ_ЗДЕСЬ", - "__Secure-3PSID": "СКОПИРОВАННОЕ_ЗНАЧЕНИЕ_ЗДЕСЬ", - "__Secure-1PAPISID": "СКОПИРОВАННОЕ_ЗНАЧЕНИЕ_ЗДЕСЬ", - "__Secure-3PAPISID": "СКОПИРОВАННОЕ_ЗНАЧЕНИЕ_ЗДЕСЬ", - "VISITOR_INFO1_LIVE": "СКОПИРОВАННОЕ_ЗНАЧЕНИЕ_ЗДЕСЬ", - "YSC": "СКОПИРОВАННОЕ_ЗНАЧЕНИЕ_ЗДЕСЬ" -} -``` - -## Шаг 3: Перезапуск сервера - -После настройки cookies перезапустите сервер: - -```bash -yarn start -``` - -В логах вы должны увидеть: -``` -YouTube cookies loaded successfully -``` - -## Примечания - -- Cookies периодически истекают, и их нужно обновлять -- Файл `youtube-cookies.json` добавлен в `.gitignore` для безопасности -- Если cookies не работают, попробуйте обновить их из браузера -- В случае проблем сервер автоматически переключится на анонимный доступ - -## Альтернативный способ - -Если cookies не помогают, можно установить системный `yt-dlp`: - -```bash -# macOS -brew install yt-dlp - -# Затем использовать через exec в Node.js -``` \ No newline at end of file diff --git a/package.json b/package.json index f6badff..432c840 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,17 @@ "name": "quixotic", "version": "1.0.0", "description": "Telegram miniapp for YouTube music search and MP3 conversion", - "main": "src/server.js", + "main": "dist/server.js", "scripts": { - "start": "node src/server.js", - "dev": "nodemon src/server.js", - "lint": "eslint src/ public/", - "lint:fix": "eslint src/ public/ --fix", - "validate": "npm run lint && node -c src/server.js && echo '✅ All checks passed!'", + "build": "tsc && tsc -p tsconfig.frontend.json", + "build:backend": "tsc", + "build:frontend": "tsc -p tsconfig.frontend.json", + "start": "node dist/server.js", + "dev": "ts-node src/server.ts", + "dev:watch": "nodemon --exec ts-node src/server.ts", + "lint": "eslint src/ public/ --ext .ts,.js", + "lint:fix": "eslint src/ public/ --ext .ts,.js --fix", + "validate": "npm run lint && npm run build && echo '✅ All checks passed!'", "pretest": "npm run validate" }, "packageManager": "yarn@1.22.19", @@ -22,8 +26,14 @@ "sqlite3": "^5.1.6" }, "devDependencies": { + "@types/express": "^5.0.3", + "@types/fluent-ffmpeg": "^2.1.27", + "@types/node": "^24.3.0", + "@types/node-telegram-bot-api": "^0.64.10", "eslint": "^9.34.0", - "nodemon": "^3.0.2" + "nodemon": "^3.0.2", + "ts-node": "^10.9.2", + "typescript": "^5.9.2" }, "engines": { "node": ">=16.0.0" diff --git a/public/index.html b/public/index.html index c0cb039..4cc912d 100644 --- a/public/index.html +++ b/public/index.html @@ -35,6 +35,6 @@ - + \ No newline at end of file diff --git a/public/script.js b/public/script.ts similarity index 73% rename from public/script.js rename to public/script.ts index f265c0c..c7457a6 100644 --- a/public/script.js +++ b/public/script.ts @@ -1,34 +1,79 @@ +interface TelegramWebApp { + ready(): void; + expand(): void; + sendData(data: string): void; + MainButton: { + show(): void; + hide(): void; + }; + initDataUnsafe?: { + user?: { + id: number; + }; + }; +} + +interface Window { + Telegram?: { + WebApp: TelegramWebApp; + }; +} + +interface VideoResult { + id: string; + title: string; + channel: string; + thumbnail: string; + duration: number; +} + +interface SearchResponse { + videos: VideoResult[]; +} + +interface ConvertResponse { + audioUrl?: string; + title: string; +} + class QuixoticApp { + private tg?: TelegramWebApp; + private searchInput!: HTMLInputElement; + private searchBtn!: HTMLButtonElement; + private loading!: HTMLElement; + private results!: HTMLElement; + private noResults!: HTMLElement; + constructor() { this.tg = window.Telegram?.WebApp; this.init(); this.bindEvents(); } - init() { + private init(): void { if (this.tg) { this.tg.ready(); this.tg.expand(); this.tg.MainButton.hide(); } - this.searchInput = document.getElementById('searchInput'); - this.searchBtn = document.getElementById('searchBtn'); - this.loading = document.getElementById('loading'); - this.results = document.getElementById('results'); - this.noResults = document.getElementById('noResults'); + this.searchInput = document.getElementById('searchInput') as HTMLInputElement; + this.searchBtn = document.getElementById('searchBtn') as HTMLButtonElement; + this.loading = document.getElementById('loading') as HTMLElement; + this.results = document.getElementById('results') as HTMLElement; + this.noResults = document.getElementById('noResults') as HTMLElement; } - bindEvents() { + private bindEvents(): void { this.searchBtn.addEventListener('click', () => this.search()); - this.searchInput.addEventListener('keypress', (e) => { + this.searchInput.addEventListener('keypress', (e: KeyboardEvent) => { if (e.key === 'Enter') { this.search(); } }); } - async search() { + private async search(): Promise { const query = this.searchInput.value.trim(); if (!query) return; @@ -50,7 +95,7 @@ class QuixoticApp { throw new Error('Search failed'); } - const data = await response.json(); + const data: SearchResponse = await response.json(); this.displayResults(data.videos); } catch (error) { console.error('Search error:', error); @@ -58,19 +103,19 @@ class QuixoticApp { } } - showLoading() { + private showLoading(): void { this.loading.classList.remove('hidden'); this.results.classList.add('hidden'); this.noResults.classList.add('hidden'); this.searchBtn.disabled = true; } - hideLoading() { + private hideLoading(): void { this.loading.classList.add('hidden'); this.searchBtn.disabled = false; } - displayResults(videos) { + private displayResults(videos: VideoResult[]): void { this.hideLoading(); if (!videos || videos.length === 0) { @@ -93,16 +138,18 @@ class QuixoticApp { this.noResults.classList.add('hidden'); } - showNoResults() { + private showNoResults(): void { this.hideLoading(); this.results.classList.add('hidden'); this.noResults.classList.remove('hidden'); } - async convertVideo(videoId, title) { + public async convertVideo(videoId: string, title: string): Promise { console.log('Convert video called:', { videoId, title }); - const videoElement = event.currentTarget; - videoElement.classList.add('converting'); + const videoElement = (event as any)?.currentTarget as HTMLElement; + if (videoElement) { + videoElement.classList.add('converting'); + } try { console.log('Sending convert request...'); @@ -123,7 +170,7 @@ class QuixoticApp { throw new Error(`Conversion failed with status: ${response.status}`); } - const data = await response.json(); + const data: ConvertResponse = await response.json(); console.log('Response data:', data); if (data.audioUrl) { @@ -152,7 +199,7 @@ class QuixoticApp { // Should not happen since we removed fallbacks throw new Error('No audio URL received'); } - } catch (error) { + } catch (error: any) { console.error('Conversion error:', error); // Show specific error message @@ -168,18 +215,20 @@ class QuixoticApp { this.showMessage(`❌ ${errorMsg}`, 'warning'); } finally { - videoElement.classList.remove('converting'); + if (videoElement) { + videoElement.classList.remove('converting'); + } } } - formatDuration(seconds) { + private formatDuration(seconds: number): string { if (!seconds) return ''; const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, '0')}`; } - showMessage(message, type = 'info') { + private showMessage(message: string, type: string = 'info'): void { // Remove existing message if any const existingMessage = document.querySelector('.status-message'); if (existingMessage) { @@ -193,7 +242,9 @@ class QuixoticApp { // Add to page const container = document.querySelector('.container'); - container.insertBefore(messageEl, container.firstChild); + if (container) { + container.insertBefore(messageEl, container.firstChild); + } // Auto-remove after 5 seconds setTimeout(() => { @@ -203,11 +254,12 @@ class QuixoticApp { }, 5000); } - escapeHtml(text) { + private escapeHtml(text: string): string { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } } -const app = new QuixoticApp(); \ No newline at end of file +const app = new QuixoticApp(); +(window as any).app = app; \ No newline at end of file diff --git a/src/bot.js b/src/bot.js deleted file mode 100644 index 39cdab5..0000000 --- a/src/bot.js +++ /dev/null @@ -1,223 +0,0 @@ -const TelegramBot = require('node-telegram-bot-api'); -const Database = require('./database'); - -class QuixoticBot { - constructor(token, webAppUrl) { - this.bot = new TelegramBot(token, { polling: true }); - this.webAppUrl = webAppUrl; - this.db = new Database(); - this.init(); - } - - init() { - console.log('> Telegram bot initialized'); - this.setupCommands(); - this.setupHandlers(); - } - - setupCommands() { - // Set bot commands - this.bot.setMyCommands([ - { command: 'start', description: '0?CAB8BL ?@8;>65=85' }, - { command: 'help', description: '><>IL' }, - { command: 'history', description: 'AB>@8O ?>8A:0' } - ]); - } - - setupHandlers() { - // Start command - this.bot.onText(/\/start/, async (msg) => { - const chatId = msg.chat.id; - const user = msg.from; - - try { - // Add user to database - await this.db.addUser(user); - - const keyboard = { - inline_keyboard: [[ - { - text: '< B:@KBL Quixotic', - web_app: { url: this.webAppUrl } - } - ]] - }; - - await this.bot.sendMessage(chatId, - '< >1@> ?>60;>20BL 2 Quixotic!\n\n' + - '0948 ;N1CN ?5A=N =0 YouTube 8 ?>;CG8 MP3 D09; ?@O<> 2 G0B.\n\n' + - '06<8 :=>?:C =865, GB>1K =0G0BL ?>8A::', - { reply_markup: keyboard } - ); - } catch (error) { - console.error('Start command error:', error); - await this.bot.sendMessage(chatId, '@>87>H;0 >H81:0. >?@>1C9B5 ?>765.'); - } - }); - - // Help command - this.bot.onText(/\/help/, async (msg) => { - const chatId = msg.chat.id; - - const helpText = `< *Quixotic - YouTube to MP3* - -*0: ?>;L7>20BLAO:* -1 06<8 :=>?:C "B:@KBL Quixotic" -2 2548 =0720=85 ?5A=8 2 ?>8A:>2CN AB@>:C -3 K15@8 =C6=K9 B@5: 87 A?8A:0 -4 >;CG8 MP3 D09; 2 G0B! - -*><0=4K:* -/start - 0?CAB8BL ?@8;>65=85 -/help - -B0 A?@02:0 -/history - AB>@8O ?>8A:0 - -*>7<>6=>AB8:* -" >8A: ?> YouTube -" KA>:>5 :0G5AB2> MP3 (128kbps) -" KAB@0O :>=25@B0F8O -" AB>@8O ?>8A:0`; - - await this.bot.sendMessage(chatId, helpText, { parse_mode: 'Markdown' }); - }); - - // History command - this.bot.onText(/\/history/, async (msg) => { - const chatId = msg.chat.id; - const userId = msg.from.id; - - try { - const user = await this.db.getUserByTelegramId(userId); - if (!user) { - await this.bot.sendMessage(chatId, 'AB>@8O ?>8A:0 ?CAB0.'); - return; - } - - // Get recent search history - const history = await this.db.db.all( - `SELECT query, created_at FROM search_history - WHERE user_id = ? - ORDER BY created_at DESC - LIMIT 10`, - [user.id] - ); - - if (history.length === 0) { - await this.bot.sendMessage(chatId, 'AB>@8O ?>8A:0 ?CAB0.'); - return; - } - - let historyText = '= *>A;54=85 ?>8A:>2K5 70?@>AK:*\n\n'; - history.forEach((item, index) => { - const date = new Date(item.created_at).toLocaleDateString('ru-RU'); - historyText += `${index + 1}. ${item.query} _(${date})_\n`; - }); - - await this.bot.sendMessage(chatId, historyText, { parse_mode: 'Markdown' }); - } catch (error) { - console.error('History command error:', error); - await this.bot.sendMessage(chatId, 'H81:0 ?>;CG5=8O 8AB>@88.'); - } - }); - - // Handle web app data - this.bot.on('web_app_data', async (msg) => { - const chatId = msg.chat.id; - const data = JSON.parse(msg.web_app.data); - - try { - if (data.action === 'send_audio') { - await this.sendAudioFile(chatId, data.audioUrl, data.title); - } - } catch (error) { - console.error('Web app data error:', error); - await this.bot.sendMessage(chatId, 'H81:0 >1@01>B:8 40==KE.'); - } - }); - - // Handle inline queries for search - this.bot.on('inline_query', async (query) => { - const queryId = query.id; - const searchQuery = query.query; - - if (!searchQuery || searchQuery.length < 3) { - await this.bot.answerInlineQuery(queryId, []); - return; - } - - try { - const YouTubeService = require('./youtube'); - const youtube = new YouTubeService(); - const videos = await youtube.searchVideos(searchQuery, 5); - - const results = videos.map((video, index) => ({ - type: 'article', - id: `${index}`, - title: video.title, - description: `${video.channel} " ${this.formatDuration(video.duration)}`, - thumb_url: video.thumbnail, - input_message_content: { - message_text: `< ${video.title}\n= ${video.url}` - } - })); - - await this.bot.answerInlineQuery(queryId, results, { - cache_time: 300, - is_personal: true - }); - } catch (error) { - console.error('Inline query error:', error); - await this.bot.answerInlineQuery(queryId, []); - } - }); - - // Error handler - this.bot.on('error', (error) => { - console.error('Telegram bot error:', error); - }); - - console.log(' Bot handlers setup complete'); - } - - async sendAudioFile(chatId, audioUrl, title) { - try { - await this.bot.sendMessage(chatId, ' >43>B02;820N MP3 D09;...'); - - // Send audio file - await this.bot.sendAudio(chatId, audioUrl, { - title: title, - performer: 'Quixotic', - caption: `< ${title}` - }); - - } catch (error) { - console.error('Send audio error:', error); - await this.bot.sendMessage(chatId, - 'L 5 C40;>AL >B?@028BL 0C48>D09;. >?@>1C9B5 5I5 @07.\n\n' + - `@O<0O AAK;:0: ${audioUrl}` - ); - } - } - - formatDuration(seconds) { - if (!seconds) return ''; - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins}:${secs.toString().padStart(2, '0')}`; - } -} - -// Initialize bot if this file is run directly -if (require.main === module) { - const token = process.env.TELEGRAM_BOT_TOKEN; - const webAppUrl = process.env.WEB_APP_URL || 'https://your-domain.com'; - - if (!token) { - console.error('L TELEGRAM_BOT_TOKEN environment variable is required'); - process.exit(1); - } - - new QuixoticBot(token, webAppUrl); -} - -module.exports = QuixoticBot; \ No newline at end of file diff --git a/src/bot.ts b/src/bot.ts new file mode 100644 index 0000000..d37f218 --- /dev/null +++ b/src/bot.ts @@ -0,0 +1,275 @@ +import TelegramBot from 'node-telegram-bot-api'; +import { Database } from './database'; + +interface TelegramUser { + id: number; + first_name: string; + last_name?: string; + username?: string; +} + +interface Message { + chat: { + id: number; + }; + from?: TelegramUser; + web_app?: { + data: string; + }; +} + +interface InlineQuery { + id: string; + query: string; +} + +interface WebAppData { + action: string; + audioUrl: string; + title: string; +} + +interface SearchResult { + query: string; + created_at: string; +} + +export class QuixoticBot { + private bot: TelegramBot; + private webAppUrl: string; + private db: Database; + + constructor(token: string, webAppUrl: string) { + this.bot = new TelegramBot(token, { polling: true }); + this.webAppUrl = webAppUrl; + this.db = new Database(); + this.init(); + } + + private init(): void { + console.log('🤖 Telegram bot initialized'); + this.setupCommands(); + this.setupHandlers(); + } + + private setupCommands(): void { + // Set bot commands + this.bot.setMyCommands([ + { command: 'start', description: 'Запустить приложение' }, + { command: 'help', description: 'Помощь' }, + { command: 'history', description: 'История поиска' } + ]); + } + + private setupHandlers(): void { + // Start command + this.bot.onText(/\/start/, async (msg: Message) => { + const chatId = msg.chat.id; + const user = msg.from; + + try { + // Add user to database + if (user) { + await this.db.addUser(user); + } + + const keyboard = { + inline_keyboard: [[ + { + text: '🎵 Открыть Quixotic', + web_app: { url: this.webAppUrl } + } + ]] + }; + + await this.bot.sendMessage(chatId, + '🎵 Добро пожаловать в Quixotic!\n\n' + + 'Найди любую песню на YouTube и получи MP3 файл прямо в чат.\n\n' + + 'Нажми кнопку ниже, чтобы начать поиск:', + { reply_markup: keyboard } + ); + } catch (error) { + console.error('Start command error:', error); + await this.bot.sendMessage(chatId, '❌ Произошла ошибка. Попробуйте позже.'); + } + }); + + // Help command + this.bot.onText(/\/help/, async (msg: Message) => { + const chatId = msg.chat.id; + + const helpText = `🎵 *Quixotic - YouTube to MP3* + +*Как пользоваться:* +1️⃣ Нажми кнопку "Открыть Quixotic" +2️⃣ Введи название песни в поисковую строку +3️⃣ Выбери нужный трек из списка +4️⃣ Получи MP3 файл в чат! + +*Команды:* +/start - Запустить приложение +/help - Эта справка +/history - История поиска + +*Возможности:* +✅ Поиск по YouTube +✅ Высокое качество MP3 (128kbps) +✅ Быстрая конвертация +✅ История поиска`; + + await this.bot.sendMessage(chatId, helpText, { parse_mode: 'Markdown' }); + }); + + // History command + this.bot.onText(/\/history/, async (msg: Message) => { + const chatId = msg.chat.id; + const userId = msg.from?.id; + + if (!userId) return; + + try { + const user = await this.db.getUserByTelegramId(userId); + if (!user) { + await this.bot.sendMessage(chatId, 'История поиска пуста.'); + return; + } + + // Get recent search history + const history = await this.getSearchHistory(user.id); + + if (history.length === 0) { + await this.bot.sendMessage(chatId, 'История поиска пуста.'); + return; + } + + let historyText = '📋 *Последние поисковые запросы:*\n\n'; + history.forEach((item, index) => { + const date = new Date(item.created_at).toLocaleDateString('ru-RU'); + historyText += `${index + 1}. ${item.query} _(${date})_\n`; + }); + + await this.bot.sendMessage(chatId, historyText, { parse_mode: 'Markdown' }); + } catch (error) { + console.error('History command error:', error); + await this.bot.sendMessage(chatId, '❌ Ошибка получения истории.'); + } + }); + + // Handle web app data + this.bot.on('web_app_data', async (msg: Message) => { + const chatId = msg.chat.id; + + if (!msg.web_app?.data) return; + + const data: WebAppData = JSON.parse(msg.web_app.data); + + try { + if (data.action === 'send_audio') { + await this.sendAudioFile(chatId, data.audioUrl, data.title); + } + } catch (error) { + console.error('Web app data error:', error); + await this.bot.sendMessage(chatId, '❌ Ошибка обработки данных.'); + } + }); + + // Handle inline queries for search + this.bot.on('inline_query', async (query: InlineQuery) => { + const queryId = query.id; + const searchQuery = query.query; + + if (!searchQuery || searchQuery.length < 3) { + await this.bot.answerInlineQuery(queryId, []); + return; + } + + try { + const { SoundCloudService } = require('./soundcloud'); + const soundcloud = new SoundCloudService(); + const videos = await soundcloud.searchTracks(searchQuery, 5); + + const results = videos.map((video: any, index: number) => ({ + type: 'article', + id: `${index}`, + title: video.title, + description: `${video.channel} • ${this.formatDuration(video.duration)}`, + thumb_url: video.thumbnail, + input_message_content: { + message_text: `🎵 ${video.title}\n🔗 ${video.url}` + } + })); + + await this.bot.answerInlineQuery(queryId, results, { + cache_time: 300, + is_personal: true + }); + } catch (error) { + console.error('Inline query error:', error); + await this.bot.answerInlineQuery(queryId, []); + } + }); + + // Error handler + this.bot.on('error', (error: Error) => { + console.error('Telegram bot error:', error); + }); + + console.log('✅ Bot handlers setup complete'); + } + + private async getSearchHistory(userId: number): Promise { + return new Promise((resolve, reject) => { + this.db['db'].all( + `SELECT query, created_at FROM search_history + WHERE user_id = ? + ORDER BY created_at DESC + LIMIT 10`, + [userId], + (err: Error | null, rows: SearchResult[]) => { + if (err) reject(err); + else resolve(rows || []); + } + ); + }); + } + + private async sendAudioFile(chatId: number, audioUrl: string, title: string): Promise { + try { + await this.bot.sendMessage(chatId, '⏳ Подготавливаю MP3 файл...'); + + // Send audio file + await this.bot.sendAudio(chatId, audioUrl, { + title: title, + performer: 'Quixotic', + caption: `🎵 ${title}` + }); + + } catch (error) { + console.error('Send audio error:', error); + await this.bot.sendMessage(chatId, + '❌ Не удалось отправить аудиофайл. Попробуйте еще раз.\n\n' + + `Прямая ссылка: ${audioUrl}` + ); + } + } + + private formatDuration(seconds: number): string { + if (!seconds) return ''; + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins}:${secs.toString().padStart(2, '0')}`; + } +} + +// Initialize bot if this file is run directly +if (require.main === module) { + const token = process.env.TELEGRAM_BOT_TOKEN; + const webAppUrl = process.env.WEB_APP_URL || 'https://your-domain.com'; + + if (!token) { + console.error('❌ TELEGRAM_BOT_TOKEN environment variable is required'); + process.exit(1); + } + + new QuixoticBot(token, webAppUrl); +} \ No newline at end of file diff --git a/src/database.js b/src/database.ts similarity index 75% rename from src/database.js rename to src/database.ts index efa6a0d..347c1fc 100644 --- a/src/database.js +++ b/src/database.ts @@ -1,14 +1,33 @@ -const sqlite3 = require('sqlite3').verbose(); -const path = require('path'); +import sqlite3 from 'sqlite3'; +import path from 'path'; + +interface TelegramUser { + id: number; + username?: string; + first_name?: string; + last_name?: string; +} + +interface User { + id: number; + telegram_id: number; + username?: string; + first_name?: string; + last_name?: string; + created_at: string; +} + +export class Database { + private dbPath: string; + private db: sqlite3.Database; -class Database { constructor() { this.dbPath = path.join(__dirname, '../database/quixotic.db'); this.db = new sqlite3.Database(this.dbPath); this.init(); } - init() { + private init(): void { this.db.serialize(() => { // Users table this.db.run(`CREATE TABLE IF NOT EXISTS users ( @@ -42,14 +61,14 @@ class Database { }); } - addUser(telegramUser) { + addUser(telegramUser: TelegramUser): Promise { return new Promise((resolve, reject) => { const { id, username, first_name, last_name } = telegramUser; this.db.run( `INSERT OR REPLACE INTO users (telegram_id, username, first_name, last_name) VALUES (?, ?, ?, ?)`, [id, username, first_name, last_name], - function(err) { + function(err: Error | null) { if (err) reject(err); else resolve(this.lastID); } @@ -57,12 +76,12 @@ class Database { }); } - addSearchHistory(userId, query) { + addSearchHistory(userId: number, query: string): Promise { return new Promise((resolve, reject) => { this.db.run( 'INSERT INTO search_history (user_id, query) VALUES (?, ?)', [userId, query], - function(err) { + function(err: Error | null) { if (err) reject(err); else resolve(this.lastID); } @@ -70,12 +89,12 @@ class Database { }); } - addDownload(userId, youtubeId, title, filePath) { + addDownload(userId: number, youtubeId: string, title: string, filePath: string): Promise { return new Promise((resolve, reject) => { this.db.run( 'INSERT INTO downloads (user_id, youtube_id, title, file_path) VALUES (?, ?, ?, ?)', [userId, youtubeId, title, filePath], - function(err) { + function(err: Error | null) { if (err) reject(err); else resolve(this.lastID); } @@ -83,12 +102,12 @@ class Database { }); } - getUserByTelegramId(telegramId) { + getUserByTelegramId(telegramId: string | number): Promise { return new Promise((resolve, reject) => { this.db.get( 'SELECT * FROM users WHERE telegram_id = ?', [telegramId], - (err, row) => { + (err: Error | null, row: User) => { if (err) reject(err); else resolve(row); } @@ -96,9 +115,7 @@ class Database { }); } - close() { + close(): void { this.db.close(); } -} - -module.exports = Database; \ No newline at end of file +} \ No newline at end of file diff --git a/src/server.js b/src/server.ts similarity index 83% rename from src/server.js rename to src/server.ts index 3dd918e..39dd930 100644 --- a/src/server.js +++ b/src/server.ts @@ -1,9 +1,9 @@ -const express = require('express'); -const path = require('path'); -const fs = require('fs'); -const ffmpeg = require('fluent-ffmpeg'); -const Database = require('./database'); -const SoundCloudService = require('./soundcloud'); +import express, { Request, Response, NextFunction } from 'express'; +import path from 'path'; +import fs from 'fs'; +import ffmpeg from 'fluent-ffmpeg'; +import { Database } from './database'; +import { SoundCloudService } from './soundcloud'; const app = express(); const port = process.env.PORT || 3000; @@ -23,14 +23,14 @@ if (!fs.existsSync(downloadsDir)) { } // Routes -app.get('/', (req, res) => { +app.get('/', (req: Request, res: Response) => { res.sendFile(path.join(__dirname, '../public/index.html')); }); // Search videos -app.post('/api/search', async (req, res) => { +app.post('/api/search', async (req: Request, res: Response) => { try { - const { query, userId } = req.body; + const { query, userId }: { query?: string; userId?: string } = req.body; if (!query || query.trim().length === 0) { return res.status(400).json({ error: 'Query is required' }); @@ -58,9 +58,9 @@ app.post('/api/search', async (req, res) => { }); // Convert video to MP3 -app.post('/api/convert', async (req, res) => { +app.post('/api/convert', async (req: Request, res: Response) => { try { - const { videoId, title, userId } = req.body; + const { videoId, title, userId }: { videoId?: string; title?: string; userId?: string } = req.body; console.log('Convert request received:', { videoId, title, userId }); if (!videoId) { @@ -68,7 +68,7 @@ app.post('/api/convert', async (req, res) => { } // Generate safe filename - const safeTitle = title.replace(/[^\w\s-]/g, '').replace(/\s+/g, '_').substring(0, 50); + const safeTitle = (title || '').replace(/[^\w\s-]/g, '').replace(/\s+/g, '_').substring(0, 50); const filename = `${videoId}_${safeTitle}.mp3`; const outputPath = path.join(downloadsDir, filename); @@ -88,7 +88,7 @@ app.post('/api/convert', async (req, res) => { console.log('Audio stream obtained, starting FFmpeg conversion...'); // Convert to MP3 using ffmpeg - await new Promise((resolve, reject) => { + await new Promise((resolve, reject) => { const conversion = ffmpeg(audioStream) .audioCodec('libmp3lame') .audioBitrate('192k') @@ -96,10 +96,10 @@ app.post('/api/convert', async (req, res) => { .audioFrequency(44100) .format('mp3') .output(outputPath) - .on('start', (command) => { + .on('start', (command: string) => { console.log('FFmpeg started:', command); }) - .on('progress', (progress) => { + .on('progress', (progress: any) => { if (progress.percent) { console.log(`Conversion progress: ${Math.round(progress.percent)}%`); } @@ -108,7 +108,7 @@ app.post('/api/convert', async (req, res) => { console.log('MP3 conversion completed successfully'); resolve(); }) - .on('error', (err) => { + .on('error', (err: Error) => { console.error('FFmpeg error:', err.message); reject(err); }); @@ -121,7 +121,7 @@ app.post('/api/convert', async (req, res) => { try { const user = await db.getUserByTelegramId(userId); if (user) { - await db.addDownload(user.id, videoId, title, outputPath); + await db.addDownload(user.id, videoId, title || '', outputPath); } } catch (dbError) { console.error('Database error:', dbError); @@ -132,7 +132,7 @@ app.post('/api/convert', async (req, res) => { console.log('Conversion successful, file available at:', audioUrl); res.json({ audioUrl, title }); - } catch (conversionError) { + } catch (conversionError: any) { console.error('Conversion failed for video:', videoId); console.error('Error details:', conversionError.message); console.error('Full error:', conversionError); @@ -155,12 +155,12 @@ app.post('/api/convert', async (req, res) => { app.use('/downloads', express.static(downloadsDir)); // Health check -app.get('/health', (req, res) => { +app.get('/health', (req: Request, res: Response) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // Error handler -app.use((err, req, res, next) => { +app.use((err: Error, req: Request, res: Response, next: NextFunction) => { console.error(err.stack); res.status(500).json({ error: 'Something went wrong!' }); }); @@ -196,4 +196,4 @@ app.listen(port, () => { console.log(`Open in browser: http://localhost:${port}`); }); -module.exports = app; \ No newline at end of file +export default app; \ No newline at end of file diff --git a/src/soundcloud.js b/src/soundcloud.ts similarity index 69% rename from src/soundcloud.js rename to src/soundcloud.ts index 27a46e9..e7d7e7e 100644 --- a/src/soundcloud.js +++ b/src/soundcloud.ts @@ -1,12 +1,44 @@ -const scdl = require('soundcloud-downloader').default; +import scdl from 'soundcloud-downloader'; +import { Readable } from 'stream'; +interface SearchTrack { + id: number; + title: string; + user?: { + username: string; + avatar_url?: string; + }; + artwork_url?: string; + duration: number; + permalink_url: string; + streamable: boolean; + downloadable: boolean; +} -class SoundCloudService { +interface TrackResult { + id: number; + title: string; + channel: string; + thumbnail: string; + duration: number; + url: string; + streamable: boolean; + downloadable: boolean; +} + +interface TrackInfo { + title: string; + author: string; + length: number; + available: boolean; +} + +export class SoundCloudService { constructor() { console.log('SoundCloud service initialized'); } - async searchTracks(query, maxResults = 10) { + async searchTracks(query: string, maxResults: number = 10): Promise { try { console.log(`Searching SoundCloud for: ${query}`); @@ -15,14 +47,14 @@ class SoundCloudService { query: query, limit: maxResults, resourceType: 'tracks' - }); + }) as any; if (!tracks || tracks.length === 0) { console.log('No tracks found'); return []; } - const trackResults = tracks.map(track => ({ + const trackResults: TrackResult[] = tracks.map(track => ({ id: track.id, title: track.title, channel: track.user?.username || 'Unknown Artist', @@ -36,15 +68,15 @@ class SoundCloudService { console.log(`Found ${trackResults.length} tracks on SoundCloud`); return trackResults; - } catch (error) { + } catch (error: any) { console.error('SoundCloud search error:', error.message); return []; } } - async getTrackInfo(trackId) { + async getTrackInfo(trackId: string | number): Promise { try { - const track = await scdl.getInfo(trackId); + const track = await scdl.getInfo(String(trackId)) as SearchTrack; return { title: track.title, author: track.user?.username || 'Unknown', @@ -57,12 +89,12 @@ class SoundCloudService { } } - async getAudioStream(trackId) { + async getAudioStream(trackId: string | number): Promise { try { console.log(`Getting audio stream for track: ${trackId}`); // Get track info first - const trackInfo = await scdl.getInfo(trackId); + const trackInfo = await scdl.getInfo(String(trackId)) as SearchTrack; if (!trackInfo.streamable) { throw new Error('Track is not streamable'); @@ -73,12 +105,12 @@ class SoundCloudService { console.log(`Duration: ${Math.floor(trackInfo.duration / 1000)}s`); // Get audio stream - const stream = await scdl.download(trackId); + const stream = await scdl.download(String(trackId)); console.log('Audio stream obtained successfully from SoundCloud'); return stream; - } catch (error) { + } catch (error: any) { console.error('SoundCloud download failed:', error.message); // Try alternative approach @@ -90,12 +122,10 @@ class SoundCloudService { console.log('Audio stream obtained with alternative method'); return stream; - } catch (fallbackError) { + } catch (fallbackError: any) { console.error('Alternative method also failed:', fallbackError.message); throw new Error(`SoundCloud download failed: ${error.message}`); } } } -} - -module.exports = SoundCloudService; \ No newline at end of file +} \ No newline at end of file diff --git a/tsconfig.frontend.json b/tsconfig.frontend.json new file mode 100644 index 0000000..b42c4ab --- /dev/null +++ b/tsconfig.frontend.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "node", + "lib": ["ES2020", "DOM"], + "outDir": "./public/dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": false, + "sourceMap": true, + "allowJs": true + }, + "include": [ + "public/script.ts" + ], + "exclude": [ + "node_modules", + "src", + "public/dist" + ] +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ed2e2ee --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "outDir": "./dist", + "rootDir": "./src", + "strict": false, + "noImplicitAny": false, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "sourceMap": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist", + "public" + ] +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7f25bc2..ab89784 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,13 @@ resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz" integrity sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@cypress/request-promise@^5.0.0": version "5.0.0" resolved "https://registry.npmjs.org/@cypress/request-promise/-/request-promise-5.0.0.tgz" @@ -17,7 +24,7 @@ stealthy-require "^1.1.1" tough-cookie "^4.1.3" -"@cypress/request@^3.0.0", "@cypress/request@^3.0.1": +"@cypress/request@^3.0.1": version "3.0.9" resolved "https://registry.npmjs.org/@cypress/request/-/request-3.0.9.tgz" integrity sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw== @@ -140,6 +147,24 @@ resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@npmcli/fs@^1.0.0": version "1.1.1" resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz" @@ -161,16 +186,149 @@ resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/body-parser@*": + version "1.19.6" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.6.tgz#1859bebb8fd7dac9918a45d54c1971ab8b5af474" + integrity sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/caseless@*": + version "0.12.5" + resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" + integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + "@types/estree@^1.0.6": version "1.0.8" resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== +"@types/express-serve-static-core@^5.0.0": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz#2fa94879c9d46b11a5df4c74ac75befd6b283de6" + integrity sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.3.tgz#6c4bc6acddc2e2a587142e1d8be0bce20757e956" + integrity sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/serve-static" "*" + +"@types/fluent-ffmpeg@^2.1.27": + version "2.1.27" + resolved "https://registry.yarnpkg.com/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.27.tgz#c4eac6fbda30bb6316d2220c8faf54f48db0812d" + integrity sha512-QiDWjihpUhriISNoBi2hJBRUUmoj/BMTYcfz+F+ZM9hHWBYABFAE6hjP/TbCZC0GWwlpa3FzvHH9RzFeRusZ7A== + dependencies: + "@types/node" "*" + +"@types/http-errors@*": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.5.tgz#5b749ab2b16ba113423feb1a64a95dcd30398472" + integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== + "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/node-telegram-bot-api@^0.64.10": + version "0.64.10" + resolved "https://registry.yarnpkg.com/@types/node-telegram-bot-api/-/node-telegram-bot-api-0.64.10.tgz#c93a2b4a7664071babde171bbda5b2e61af8d5d6" + integrity sha512-hiwjeze0TGVB7f5Es6WnLqEsd3g3TRbR2SLeT7iAtO2rrkisCjc4N65wFXzkp+viIReV7xun2/j/PHTf+Hcaqg== + dependencies: + "@types/node" "*" + "@types/request" "*" + +"@types/node@*", "@types/node@^24.3.0": + version "24.3.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.3.0.tgz#89b09f45cb9a8ee69466f18ee5864e4c3eb84dec" + integrity sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow== + dependencies: + undici-types "~7.10.0" + +"@types/qs@*": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5" + integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/request@*": + version "2.48.13" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.13.tgz#abdf4256524e801ea8fdda54320f083edb5a6b80" + integrity sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg== + dependencies: + "@types/caseless" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + form-data "^2.5.5" + +"@types/send@*": + version "0.17.5" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.5.tgz#d991d4f2b16f2b1ef497131f00a9114290791e74" + integrity sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.8" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.8.tgz#8180c3fbe4a70e8f00b9f70b9ba7f08f35987877" + integrity sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/tough-cookie@*": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== + abbrev@1: version "1.1.1" resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" @@ -189,12 +347,19 @@ acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.15.0: +acorn-walk@^8.1.1: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.15.0, acorn@^8.4.1: version "8.15.0" resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== -agent-base@^6.0.2, agent-base@6: +agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -216,7 +381,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -259,6 +424,11 @@ are-we-there-yet@^3.0.0: delegates "^1.0.0" readable-stream "^3.6.0" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" @@ -309,7 +479,7 @@ asn1@~0.2.3: dependencies: safer-buffer "~2.1.0" -assert-plus@^1.0.0, assert-plus@1.0.0: +assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== @@ -574,7 +744,7 @@ color-support@^1.1.3: resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -613,15 +783,20 @@ cookie@0.7.1: resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-spawn@^7.0.6: version "7.0.6" @@ -671,20 +846,6 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@4: - version "4.4.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== - dependencies: - ms "^2.1.3" - debug@2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" @@ -692,6 +853,20 @@ debug@2.6.9: dependencies: ms "2.0.0" +debug@4, debug@^4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3: + version "4.4.1" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" @@ -752,6 +927,11 @@ detect-libc@^2.0.0: resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz" integrity sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + dotenv@^8.2.0: version "8.6.0" resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz" @@ -949,7 +1129,7 @@ eslint-visitor-keys@^4.2.1: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== -"eslint@^6.0.0 || ^7.0.0 || >=8.0.0", eslint@^9.34.0: +eslint@^9.34.0: version "9.34.0" resolved "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz" integrity sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg== @@ -1080,16 +1260,16 @@ extend@~3.0.2: resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - extsprintf@1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -1196,6 +1376,18 @@ forever-agent@~0.6.1: resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== +form-data@^2.5.5: + version "2.5.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.5.tgz#a5f6364ad7e4e67e95b4a07e2d8c6f711c74f624" + integrity sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.35" + safe-buffer "^5.2.1" + form-data@^4.0.4, form-data@~4.0.4: version "4.0.4" resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz" @@ -1207,15 +1399,6 @@ form-data@^4.0.4, form-data@~4.0.4: hasown "^2.0.2" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - formdata-polyfill@^4.0.10: version "4.0.10" resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz" @@ -1385,19 +1568,6 @@ graceful-fs@^4.2.6: resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" - integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - has-bigints@^1.0.2: version "1.1.0" resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz" @@ -1476,15 +1646,6 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" - integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - http-signature@~1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz" @@ -1509,13 +1670,6 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@^0.6.2: - version "0.6.3" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -1523,6 +1677,13 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" @@ -1569,7 +1730,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@2, inherits@2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1852,16 +2013,6 @@ json-stringify-safe@~5.0.1: resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - jsprim@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz" @@ -1919,6 +2070,11 @@ m3u8stream@^0.8.0: miniget "^4.2.2" sax "^1.2.4" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + make-fetch-happen@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz" @@ -1966,14 +2122,14 @@ mime-db@1.52.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" -mime@^1.6.0, mime@1.6.0: +mime@1.6.0, mime@^1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -2069,16 +2225,16 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -ms@^2.0.0, ms@^2.1.1, ms@^2.1.3, ms@2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + napi-build-utils@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz" @@ -2089,16 +2245,16 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -negotiator@^0.6.2: - version "0.6.4" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz" - integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== - negotiator@0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^0.6.2: + version "0.6.4" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + node-abi@^3.3.0: version "3.75.0" resolved "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz" @@ -2194,11 +2350,6 @@ npmlog@^6.0.0: gauge "^4.0.3" set-blocking "^2.0.0" -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz" @@ -2378,7 +2529,7 @@ proxy-from-env@^1.1.0: resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -psl@^1.1.28, psl@^1.1.33: +psl@^1.1.33: version "1.15.0" resolved "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz" integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== @@ -2411,11 +2562,6 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -qs@~6.5.2: - version "6.5.3" - resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - qs@6.13.0: version "6.13.0" resolved "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz" @@ -2522,32 +2668,6 @@ request-promise-core@1.1.3: dependencies: lodash "^4.17.15" -request@^2.34: - version "2.88.2" - resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" @@ -2581,17 +2701,12 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0, safe-buffer@5.2.1: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@~5.1.1: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -2613,7 +2728,7 @@ safe-regex-test@^1.1.0: es-errors "^1.3.0" is-regex "^1.2.1" -safer-buffer@^2.0.2, safer-buffer@^2.1.0, "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -2826,7 +2941,7 @@ sqlite3@^5.1.6: optionalDependencies: node-gyp "8.x" -sshpk@^1.18.0, sshpk@^1.7.0: +sshpk@^1.18.0: version "1.18.0" resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz" integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== @@ -2866,20 +2981,6 @@ stop-iteration-iterator@^1.1.0: es-errors "^1.3.0" internal-slot "^1.1.0" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" @@ -2921,6 +3022,20 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -3031,13 +3146,24 @@ tough-cookie@^5.0.0: dependencies: tldts "^6.1.32" -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: - psl "^1.1.28" - punycode "^2.1.1" + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" tunnel-agent@^0.6.0: version "0.6.0" @@ -3111,6 +3237,11 @@ typed-array-length@^1.0.7: possible-typed-array-names "^1.0.0" reflect.getprototypeof "^1.0.6" +typescript@^5.9.2: + version "5.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" + integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== + unbox-primitive@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz" @@ -3126,6 +3257,11 @@ undefsafe@^2.0.5: resolved "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== +undici-types@~7.10.0: + version "7.10.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" + integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag== + unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz" @@ -3145,7 +3281,7 @@ universalify@^0.2.0: resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -3175,16 +3311,16 @@ utils-merge@1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + vary@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" @@ -3264,14 +3400,7 @@ which@^1.1.1: dependencies: isexe "^2.0.0" -which@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -which@^2.0.2: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -3300,6 +3429,11 @@ yallist@^4.0.0: resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"