diff --git a/.env b/.env index fba692b..f5e1726 100644 --- a/.env +++ b/.env @@ -10,10 +10,12 @@ LOG_LEVEL=info DATA_SERVICE_PORT=2001 # Queue and Worker Configuration -WORKER_COUNT=4 -WORKER_CONCURRENCY=20 +WORKER_COUNT=1 +WORKER_CONCURRENCY=4 WEBSHARE_API_KEY=y8ay534rcbybdkk3evnzmt640xxfhy7252ce2t98 +WEBSHARE_ROTATING_PROXY_URL=http://doimvbnb-rotate:w5fpiwrb9895@p.webshare.io:80/ +WEBSHARE_API_URL=https://proxy.webshare.io/api/v2/ # =========================================== # DATABASE CONFIGURATIONS diff --git a/apps/data-service/package.json b/apps/data-service/package.json index 7ce4e6d..a488acc 100644 --- a/apps/data-service/package.json +++ b/apps/data-service/package.json @@ -18,10 +18,12 @@ "@stock-bot/http": "*", "@stock-bot/logger": "*", "@stock-bot/mongodb-client": "*", - "@stock-bot/queue": "*", "@stock-bot/questdb-client": "*", + "@stock-bot/queue": "*", "@stock-bot/shutdown": "*", "@stock-bot/types": "*", + "chromium-bidi": "^5.3.1", + "electron": "^36.4.0", "hono": "^4.0.0", "p-limit": "^6.2.0", "ws": "^8.0.0" diff --git a/apps/data-service/src/index.ts b/apps/data-service/src/index.ts index c3b1b10..93befc3 100644 --- a/apps/data-service/src/index.ts +++ b/apps/data-service/src/index.ts @@ -50,6 +50,10 @@ function createDataServiceQueueManager(): QueueManager { }, providers: [ // Import and initialize providers lazily + async () => { + const { initializeWebShareProvider } = await import('./providers/webshare.provider'); + return initializeWebShareProvider(); + }, async () => { const { initializeIBProvider } = await import('./providers/ib.provider'); return initializeIBProvider(); @@ -58,6 +62,10 @@ function createDataServiceQueueManager(): QueueManager { const { initializeProxyProvider } = await import('./providers/proxy.provider'); return initializeProxyProvider(); }, + async () => { + const { initializeQMProvider } = await import('./providers/qm.provider'); + return initializeQMProvider(); + }, async () => { const { initializeExchangeSyncProvider } = await import( './providers/exchange-sync.provider' diff --git a/apps/data-service/src/providers/proxy.provider.ts b/apps/data-service/src/providers/proxy.provider.ts index 657b3d4..40ddf64 100644 --- a/apps/data-service/src/providers/proxy.provider.ts +++ b/apps/data-service/src/providers/proxy.provider.ts @@ -76,7 +76,7 @@ export function initializeProxyProvider() { operation: 'fetch-from-sources', payload: {}, cronPattern: '0 0 * * 0', // Every week at midnight on Sunday - priority: 5, + priority: 0, description: 'Fetch and validate proxy list from sources', // immediately: true, // Don't run immediately during startup to avoid conflicts }, diff --git a/apps/data-service/src/providers/qm.provider.ts b/apps/data-service/src/providers/qm.provider.ts index 8ca8ccd..5e82c26 100644 --- a/apps/data-service/src/providers/qm.provider.ts +++ b/apps/data-service/src/providers/qm.provider.ts @@ -1,182 +1,68 @@ import { getLogger } from '@stock-bot/logger'; -import { ProviderConfig } from '../services/provider-registry.service'; +import { providerRegistry, type ProviderConfigWithSchedule } from '@stock-bot/queue'; const logger = getLogger('qm-provider'); -export const qmProvider: ProviderConfig = { - name: 'qm', - operations: { - 'live-data': async (payload: { symbol: string; fields?: string[] }) => { - logger.info('Fetching live data from qm', { symbol: payload.symbol }); +// Initialize and register the IB provider +export function initializeQMProvider() { + logger.info('Registering IB provider with scheduled jobs...'); - // Simulate qm API call - const mockData = { - symbol: payload.symbol, - price: Math.random() * 1000 + 100, - volume: Math.floor(Math.random() * 1000000), - change: (Math.random() - 0.5) * 20, - changePercent: (Math.random() - 0.5) * 5, - timestamp: new Date().toISOString(), - source: 'qm', - fields: payload.fields || ['price', 'volume', 'change'], - }; + const qmProviderConfig: ProviderConfigWithSchedule = { + name: 'qm', + operations: { + 'create-sessions': async () => { + logger.info('Creating QM sessions...'); + const { createSessions } = await import('./qm.tasks'); + await createSessions(); + logger.info('QM sessions created successfully'); + return { success: true, message: 'QM sessions created successfully' }; + }, + 'search-symbols': async () => { + logger.info('Starting QM symbol search...'); + const { fetchSymbols } = await import('./qm.tasks'); + const symbols = await fetchSymbols(); - // Simulate network delay - await new Promise(resolve => setTimeout(resolve, 100 + Math.random() * 200)); - - return mockData; + if (symbols && symbols.length > 0) { + logger.info('QM symbol search completed successfully', { count: symbols.length }); + return { + success: true, + message: 'QM symbol search completed successfully', + count: symbols.length, + symbols: symbols.slice(0, 10), // Return first 10 symbols as sample + }; + } else { + logger.warn('QM symbol search returned no results'); + return { + success: false, + message: 'No symbols found', + count: 0, + }; + } + }, }, - 'historical-data': async (payload: { - symbol: string; - from: Date; - to: Date; - interval?: string; - fields?: string[]; - }) => { - logger.info('Fetching historical data from qm', { - symbol: payload.symbol, - from: payload.from, - to: payload.to, - interval: payload.interval || '1d', - }); + scheduledJobs: [ + { + type: 'create-sessions', + operation: 'create-sessions', + payload: {}, + cronPattern: '*/15 * * * * *', // Every minute + priority: 7, + immediately: true, + description: 'Create and maintain QM sessions', + }, + { + type: 'search-symbols', + operation: 'search-symbols', + payload: {}, + cronPattern: '*/1 * * * *', // Every minute + priority: 10, + immediately: false, + description: 'Comprehensive symbol search using QM API', + }, + ], + }; - // Generate mock historical data - const days = Math.ceil( - (payload.to.getTime() - payload.from.getTime()) / (1000 * 60 * 60 * 24) - ); - const data = []; - - for (let i = 0; i < Math.min(days, 100); i++) { - const date = new Date(payload.from.getTime() + i * 24 * 60 * 60 * 1000); - data.push({ - date: date.toISOString().split('T')[0], - open: Math.random() * 1000 + 100, - high: Math.random() * 1000 + 100, - low: Math.random() * 1000 + 100, - close: Math.random() * 1000 + 100, - volume: Math.floor(Math.random() * 1000000), - source: 'qm', - }); - } - - // Simulate network delay - await new Promise(resolve => setTimeout(resolve, 200 + Math.random() * 300)); - - return { - symbol: payload.symbol, - interval: payload.interval || '1d', - data, - source: 'qm', - totalRecords: data.length, - }; - }, - 'batch-quotes': async (payload: { symbols: string[]; fields?: string[] }) => { - logger.info('Fetching batch quotes from qm', { - symbols: payload.symbols, - count: payload.symbols.length, - }); - - const quotes = payload.symbols.map(symbol => ({ - symbol, - price: Math.random() * 1000 + 100, - volume: Math.floor(Math.random() * 1000000), - change: (Math.random() - 0.5) * 20, - timestamp: new Date().toISOString(), - source: 'qm', - })); - - // Simulate network delay - await new Promise(resolve => setTimeout(resolve, 300 + Math.random() * 200)); - - return { - quotes, - source: 'qm', - timestamp: new Date().toISOString(), - totalSymbols: payload.symbols.length, - }; - }, - 'company-profile': async (payload: { symbol: string }) => { - logger.info('Fetching company profile from qm', { symbol: payload.symbol }); - - // Simulate company profile data - const profile = { - symbol: payload.symbol, - companyName: `${payload.symbol} Corporation`, - sector: 'Technology', - industry: 'Software', - description: `${payload.symbol} is a leading technology company.`, - marketCap: Math.floor(Math.random() * 1000000000000), - employees: Math.floor(Math.random() * 100000), - website: `https://www.${payload.symbol.toLowerCase()}.com`, - source: 'qm', - }; - - await new Promise(resolve => setTimeout(resolve, 150 + Math.random() * 100)); - - return profile; - }, - 'options-chain': async (payload: { symbol: string; expiration?: string }) => { - logger.info('Fetching options chain from qm', { - symbol: payload.symbol, - expiration: payload.expiration, - }); - - // Generate mock options data - const strikes = Array.from({ length: 20 }, (_, i) => 100 + i * 5); - const calls = strikes.map(strike => ({ - strike, - bid: Math.random() * 10, - ask: Math.random() * 10 + 0.5, - volume: Math.floor(Math.random() * 1000), - openInterest: Math.floor(Math.random() * 5000), - })); - - const puts = strikes.map(strike => ({ - strike, - bid: Math.random() * 10, - ask: Math.random() * 10 + 0.5, - volume: Math.floor(Math.random() * 1000), - openInterest: Math.floor(Math.random() * 5000), - })); - - await new Promise(resolve => setTimeout(resolve, 400 + Math.random() * 300)); - return { - symbol: payload.symbol, - expiration: - payload.expiration || - new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], - calls, - puts, - source: 'qm', - }; - }, - }, - - scheduledJobs: [ - // { - // type: 'qm-premium-refresh', - // operation: 'batch-quotes', - // payload: { symbols: ['AAPL', 'GOOGL', 'MSFT'] }, - // cronPattern: '*/2 * * * *', // Every 2 minutes - // priority: 7, - // description: 'Refresh premium quotes with detailed market data' - // }, - // { - // type: 'qm-options-update', - // operation: 'options-chain', - // payload: { symbol: 'SPY' }, - // cronPattern: '*/10 * * * *', // Every 10 minutes - // priority: 5, - // description: 'Update options chain data for SPY ETF' - // }, - // { - // type: 'qm-profiles', - // operation: 'company-profile', - // payload: { symbol: 'AAPL' }, - // cronPattern: '0 9 * * 1-5', // Weekdays at 9 AM - // priority: 3, - // description: 'Update company profile data' - // } - ], -}; + providerRegistry.registerWithSchedule(qmProviderConfig); + logger.info('IB provider registered successfully with scheduled jobs'); +} diff --git a/apps/data-service/src/providers/qm.tasks.ts b/apps/data-service/src/providers/qm.tasks.ts new file mode 100644 index 0000000..eb3c73c --- /dev/null +++ b/apps/data-service/src/providers/qm.tasks.ts @@ -0,0 +1,220 @@ +import { getRandomUserAgent } from '@stock-bot/http'; +import { getLogger } from '@stock-bot/logger'; +import { SymbolSearchUtil } from '../utils/symbol-search.util'; +import { getProxy } from './webshare.provider'; + +// Shared instances (module-scoped, not global) +let isInitialized = false; // Track if resources are initialized +let logger: ReturnType; +// let cache: CacheProvider; + +export interface QMSession { + proxy: string; + headers: Record; + successfulCalls: number; + failedCalls: number; + lastUsed: Date; +} +function getQmHeaders(): Record { + return { + 'User-Agent': getRandomUserAgent(), + Accept: '*/*', + 'Accept-Language': 'en', + 'Sec-Fetch-Mode': 'cors', + Origin: 'https://www.quotemedia.com', + Referer: 'https://www.quotemedia.com/', + }; +} + +const sessionCache: Record = { + // '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9': [], //4488d072b + // cc1cbdaf040f76db8f4c94f7d156b9b9b716e1a7509ec9c74a48a47f6b6b9f87: [], //97ff00cf3 // getQuotes + // '74963ff42f1db2320d051762b5d3950ff9eab23f9d5c5b592551b4ca0441d086': [], //32ca24e394b // getSplitsBySymbol getBrokerRatingsBySymbol getDividendsBySymbol getEarningsSurprisesBySymbol getEarningsEventsBySymbol + // '1e1d7cb1de1fd2fe52684abdea41a446919a5fe12776dfab88615ac1ce1ec2f6': [], //fb5721812d2c // getEnhancedQuotes getProfiles + // a900a06cc6b3e8036afb9eeb1bbf9783f0007698ed8f5cb1e373dc790e7be2e5: [], //cc882cd95f9 // getEnhancedQuotes + // a863d519e38f80e45d10e280fb1afc729816e23f0218db2f3e8b23005a9ad8dd: [], //05a09a41225 // getCompanyFilings getEnhancedQuotes + // b3cdb1873f3682c5aeeac097be6181529bfb755945e5a412a24f4b9316291427: [], //6a63f56a6 // getHeadlinesTickerStory + dc8c9930437f65d30f6597768800957017bac203a0a50342932757c8dfa158d6: [], //fceb3c4bdd // lookup + // '97b24911d7b034620aafad9441afdb2bc906ee5c992d86933c5903254ca29709': [], //c56424868d // detailed-quotes + // '8a394f09cb8540c8be8988780660a7ae5b583c331a1f6cb12834f051a0169a8f': [], //2a86d214e50e5 // getGlobalIndustrySectorPeers getKeyRatiosBySymbol getGlobalIndustrySectorCodeList + // '2f059f75e2a839437095c9e7e4991d2365bafa7bbb086672a87ae0cf8d92eb01': [], // 48fa36d // getNethouseBySymbol + // d7ae7e0091dd1d7011948c3dc4af09b5ec552285d92bb188be2618968bc78e3f: [], // 63548ee //getRecentTradesBySymbol getQuotes getLevel2Quote getRecentTradesBySymbol + // d22d1db8f67fe6e420b4028e5129b289ca64862aa6cee8459193747b68c01de3: [], // 84e9e + // '6e0b22a7cbc02ac3fa07d45e2880b7696aaebeb29574dce81789e570570c9002': [], // +}; + +export async function initializeQMResources(): Promise { + // Skip if already initialized + if (isInitialized) { + return; + } + logger = getLogger('qm-tasks'); + isInitialized = true; +} + +export async function createSessions(): Promise { + try { + //for each session, check array length, if less than 5, create new session + if (!isInitialized) { + await initializeQMResources(); + } + logger.info('Creating QM sessions...'); + for (const [sessionId, sessionArray] of Object.entries(sessionCache)) { + // remove any sessions with failedCalls > 10 + // const filteredArray = sessionArray.filter(session => session.failedCalls <= 10); + // sessionCache[sessionId] = filteredArray; + // if sessionArray is empty or has less than 5 sessions, create a new session + while (sessionArray.length < 2) { + logger.info(`Creating new session for ${sessionId}`); + const proxy = getProxy(); + if (proxy === null) { + logger.error('No proxy available for QM session creation'); + break; // Skip session creation if no proxy is available + } + const newSession: QMSession = { + proxy: proxy, // Placeholder, should be set to a valid proxy + headers: getQmHeaders(), + successfulCalls: 0, + failedCalls: 0, + lastUsed: new Date(), + }; + const sessionResponse = await fetch( + `https://app.quotemedia.com/auth/g/authenticate/dataTool/v0/500/${sessionId}`, + { + method: 'GET', + proxy: newSession.proxy, + headers: newSession.headers, + } + ); + + logger.debug('Session response received', { + status: sessionResponse.status, + sessionId, + }); + if (!sessionResponse.ok) { + logger.error('Failed to create QM session', { + sessionId, + status: sessionResponse.status, + statusText: sessionResponse.statusText, + }); + continue; // Skip this session if creation failed + } + const sessionData = await sessionResponse.json(); + logger.info('QM session created successfully', { + sessionId, + sessionData, + }); + newSession.headers['Datatool-Token'] = sessionData.token; + console.log(newSession.headers); + sessionArray.push(newSession); + } + } + return undefined; + } catch (error) { + logger.error('❌ Failed to fetch QM session', { error }); + return undefined; + } +} + +// API call function to search symbols via QM +async function searchQMSymbolsAPI(query: string): Promise { + const proxy = getProxy(); + + if (!proxy) { + throw new Error('No proxy available for QM API call'); + } + const sessionId = 'dc8c9930437f65d30f6597768800957017bac203a0a50342932757c8dfa158d6'; // Use the session ID for symbol lookup + const session = + sessionCache[sessionId][Math.floor(Math.random() * sessionCache[sessionId].length)]; // lookup session + if (!session) { + throw new Error(`No active session found for QM API with ID: ${sessionId}`); + } + try { + // QM lookup endpoint for symbol search + const apiUrl = `https://app.quotemedia.com/datatool/lookup.json?marketType=equity&pathName=%2Fdemo%2Fportal%2Fcompany-summary.php&q=${encodeURIComponent(query)}&qmodTool=SmartSymbolLookup&searchType=symbol&showFree=false&showHisa=false&webmasterId=500`; + + const response = await fetch(apiUrl, { + method: 'GET', + headers: session.headers, + proxy: session.proxy, + }); + + if (!response.ok) { + throw new Error(`QM API request failed: ${response.status} ${response.statusText}`); + } + + const symbols = await response.json(); + + logger.info(`QM API returned ${symbols.length} symbols for query: ${query}`); + return symbols; + } catch (error) { + logger.error(`Error searching QM symbols for query "${query}":`, error); + throw error; + } +} + +export async function fetchSymbols(): Promise { + try { + if (!isInitialized) { + await initializeQMResources(); + } + const sessionId = 'dc8c9930437f65d30f6597768800957017bac203a0a50342932757c8dfa158d6'; // Use the session ID for symbol lookup + + const currentSessions = sessionCache[sessionId] || []; + if (currentSessions.length === 0) { + logger.info('No sessions found, creating sessions first...'); + await createSessions(); + + // Wait a bit for sessions to be ready + await new Promise(resolve => setTimeout(resolve, 2000)); + + const newSessions = sessionCache[sessionId] || []; + if (newSessions.length === 0) { + throw new Error('Failed to create sessions before symbol search'); + } + logger.info(`Created ${newSessions.length} sessions for symbol search`); + } + + logger.info('🔄 Starting QM symbols fetch...'); + + // Create search function that uses our QM API + const searchFunction = async (query: string): Promise => { + return await searchQMSymbolsAPI(query); + }; + + // Use the utility to perform comprehensive search + const symbols = await SymbolSearchUtil.search( + searchFunction, + 50, // threshold + 4, // max depth (A -> AA -> AAA -> AAAA) + 200 // delay between requests in ms + ); + + logger.info(`QM symbols fetch completed. Found ${symbols.length} total symbols`); + return symbols; + } catch (error) { + logger.error('❌ Failed to fetch QM symbols', { error }); + return null; + } +} + +export async function fetchExchanges(): Promise { + try { + if (!isInitialized) { + await initializeQMResources(); + } + + logger.info('🔄 QM exchanges fetch - not implemented yet'); + // TODO: Implement QM exchanges fetching logic + return null; + } catch (error) { + logger.error('❌ Failed to fetch QM exchanges', { error }); + return null; + } +} + +export const qmTasks = { + createSessions, + fetchSymbols, + fetchExchanges, +}; diff --git a/apps/data-service/src/providers/webshare.provider.ts b/apps/data-service/src/providers/webshare.provider.ts new file mode 100644 index 0000000..2b6f6f5 --- /dev/null +++ b/apps/data-service/src/providers/webshare.provider.ts @@ -0,0 +1,151 @@ +/** + * WebShare Provider for proxy management + */ +import { getLogger } from '@stock-bot/logger'; +import type { ProviderConfigWithSchedule } from '@stock-bot/queue'; +import { providerRegistry } from '@stock-bot/queue'; + +const logger = getLogger('webshare-provider'); + +// In-memory proxy storage +let proxies: string[] = []; +let lastFetchTime: Date | null = null; +let currentProxyIndex = 0; + +export function getProxy(): string | null { + if (proxies.length === 0) { + return null; + } + + const proxy = proxies[currentProxyIndex]; + currentProxyIndex = (currentProxyIndex + 1) % proxies.length; + return proxy; +} + +// Initialize and register the WebShare provider +export function initializeWebShareProvider() { + logger.info('Registering WebShare provider with scheduled jobs...'); + + const webShareProviderConfig: ProviderConfigWithSchedule = { + name: 'webshare', + + operations: { + 'fetch-proxies': async _payload => { + logger.info('Fetching proxies from WebShare API'); + + try { + const fetchedProxies = await fetchProxiesFromWebShare(); + + if (fetchedProxies && fetchedProxies.length > 0) { + proxies = fetchedProxies; + lastFetchTime = new Date(); + + logger.info('Successfully updated proxy list', { + count: proxies.length, + lastFetchTime: lastFetchTime.toISOString(), + }); + + return { + success: true, + count: proxies.length, + lastFetchTime: lastFetchTime.toISOString(), + }; + } else { + logger.warn('No proxies fetched from WebShare API'); + return { + success: false, + count: 0, + error: 'No proxies returned from API', + }; + } + } catch (error) { + logger.error('Failed to fetch proxies from WebShare', { error }); + return { + success: false, + count: proxies.length, + error: error instanceof Error ? error.message : 'Unknown error', + }; + } + }, + }, + + scheduledJobs: [ + { + type: 'fetch-proxies', + operation: 'fetch-proxies', + payload: {}, + description: 'Fetch proxies from WebShare API', + cronPattern: '*/5 * * * *', // Every 5 minutes + priority: 2, + immediately: true, // Fetch immediately on startup + }, + ], + }; + + // Register the provider + providerRegistry.registerWithSchedule(webShareProviderConfig); + + logger.info('WebShare provider registered successfully'); +} + +export const webShareProvider = { + initialize: initializeWebShareProvider, + getProxy, +}; + +async function fetchProxiesFromWebShare(): Promise { + try { + const apiKey = process.env.WEBSHARE_API_KEY; + const apiUrl = process.env.WEBSHARE_API_URL; + + if (!apiKey || !apiUrl) { + logger.error('Missing WebShare configuration', { + hasApiKey: !!apiKey, + hasApiUrl: !!apiUrl, + }); + return null; + } + + logger.info('Fetching proxies from WebShare API'); + + const response = await fetch(`${apiUrl}proxy/list/?mode=direct&page=1&page_size=100`, { + method: 'GET', + headers: { + Authorization: `Token ${apiKey}`, + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + logger.error('WebShare API request failed', { + status: response.status, + statusText: response.statusText, + }); + return null; + } + + const data = await response.json(); + + if (!data.results || !Array.isArray(data.results)) { + logger.error('Invalid response format from WebShare API', { data }); + return null; + } + + // Transform proxy data to the format: http://username:password@host:port + const fetchedProxies = data.results.map( + (proxy: { username: string; password: string; proxy_address: string; port: number }) => { + return `http://${proxy.username}:${proxy.password}@${proxy.proxy_address}:${proxy.port}`; + } + ); + + logger.info('Successfully fetched proxies from WebShare', { + count: fetchedProxies.length, + total: data.count || fetchedProxies.length, + }); + // console.log('Fetched Proxies:', fetchedProxies); + return fetchedProxies; + } catch (error) { + logger.error('Failed to fetch proxies from WebShare', { error }); + return null; + } +} diff --git a/apps/data-service/src/providers/yahoo.provider.ts b/apps/data-service/src/providers/yahoo.provider.ts deleted file mode 100644 index 389479a..0000000 --- a/apps/data-service/src/providers/yahoo.provider.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { getLogger } from '@stock-bot/logger'; -import { ProviderConfig } from '../services/provider-registry.service'; - -const logger = getLogger('yahoo-provider'); - -export const yahooProvider: ProviderConfig = { - name: 'yahoo-finance', - operations: { - 'live-data': async (payload: { symbol: string; modules?: string[] }) => { - logger.info('Fetching live data from Yahoo Finance', { symbol: payload.symbol }); - - // Simulate Yahoo Finance API call - const mockData = { - symbol: payload.symbol, - regularMarketPrice: Math.random() * 1000 + 100, - regularMarketVolume: Math.floor(Math.random() * 1000000), - regularMarketChange: (Math.random() - 0.5) * 20, - regularMarketChangePercent: (Math.random() - 0.5) * 5, - preMarketPrice: Math.random() * 1000 + 100, - postMarketPrice: Math.random() * 1000 + 100, - marketCap: Math.floor(Math.random() * 1000000000000), - peRatio: Math.random() * 50 + 5, - dividendYield: Math.random() * 0.1, - fiftyTwoWeekHigh: Math.random() * 1200 + 100, - fiftyTwoWeekLow: Math.random() * 800 + 50, - timestamp: Date.now() / 1000, - source: 'yahoo-finance', - modules: payload.modules || ['price', 'summaryDetail'], - }; - - // Simulate network delay - await new Promise(resolve => setTimeout(resolve, 150 + Math.random() * 250)); - - return mockData; - }, - - 'historical-data': async (payload: { - symbol: string; - period1: number; - period2: number; - interval?: string; - events?: string; - }) => { - const { getLogger } = await import('@stock-bot/logger'); - const logger = getLogger('yahoo-provider'); - - logger.info('Fetching historical data from Yahoo Finance', { - symbol: payload.symbol, - period1: payload.period1, - period2: payload.period2, - interval: payload.interval || '1d', - }); - - // Generate mock historical data - const days = Math.ceil((payload.period2 - payload.period1) / (24 * 60 * 60)); - const data = []; - - for (let i = 0; i < Math.min(days, 100); i++) { - const timestamp = payload.period1 + i * 24 * 60 * 60; - data.push({ - timestamp, - date: new Date(timestamp * 1000).toISOString().split('T')[0], - open: Math.random() * 1000 + 100, - high: Math.random() * 1000 + 100, - low: Math.random() * 1000 + 100, - close: Math.random() * 1000 + 100, - adjClose: Math.random() * 1000 + 100, - volume: Math.floor(Math.random() * 1000000), - source: 'yahoo-finance', - }); - } - - // Simulate network delay - await new Promise(resolve => setTimeout(resolve, 250 + Math.random() * 350)); - - return { - symbol: payload.symbol, - interval: payload.interval || '1d', - timestamps: data.map(d => d.timestamp), - indicators: { - quote: [ - { - open: data.map(d => d.open), - high: data.map(d => d.high), - low: data.map(d => d.low), - close: data.map(d => d.close), - volume: data.map(d => d.volume), - }, - ], - adjclose: [ - { - adjclose: data.map(d => d.adjClose), - }, - ], - }, - source: 'yahoo-finance', - totalRecords: data.length, - }; - }, - search: async (payload: { query: string; quotesCount?: number; newsCount?: number }) => { - const { getLogger } = await import('@stock-bot/logger'); - const logger = getLogger('yahoo-provider'); - - logger.info('Searching Yahoo Finance', { query: payload.query }); - - // Generate mock search results - const quotes = Array.from({ length: payload.quotesCount || 5 }, (_, i) => ({ - symbol: `${payload.query.toUpperCase()}${i}`, - shortname: `${payload.query} Company ${i}`, - longname: `${payload.query} Corporation ${i}`, - exchDisp: 'NASDAQ', - typeDisp: 'Equity', - source: 'yahoo-finance', - })); - - const news = Array.from({ length: payload.newsCount || 3 }, (_, i) => ({ - uuid: `news-${i}-${Date.now()}`, - title: `${payload.query} News Article ${i}`, - publisher: 'Financial News', - providerPublishTime: Date.now() - i * 3600000, - type: 'STORY', - source: 'yahoo-finance', - })); - - await new Promise(resolve => setTimeout(resolve, 200 + Math.random() * 200)); - - return { - quotes, - news, - totalQuotes: quotes.length, - totalNews: news.length, - source: 'yahoo-finance', - }; - }, - financials: async (payload: { symbol: string; type?: 'income' | 'balance' | 'cash' }) => { - const { getLogger } = await import('@stock-bot/logger'); - const logger = getLogger('yahoo-provider'); - - logger.info('Fetching financials from Yahoo Finance', { - symbol: payload.symbol, - type: payload.type || 'income', - }); - - // Generate mock financial data - const financials = { - symbol: payload.symbol, - type: payload.type || 'income', - currency: 'USD', - annual: Array.from({ length: 4 }, (_, i) => ({ - fiscalYear: 2024 - i, - revenue: Math.floor(Math.random() * 100000000000), - netIncome: Math.floor(Math.random() * 10000000000), - totalAssets: Math.floor(Math.random() * 500000000000), - totalDebt: Math.floor(Math.random() * 50000000000), - })), - quarterly: Array.from({ length: 4 }, (_, i) => ({ - fiscalQuarter: `Q${4 - i} 2024`, - revenue: Math.floor(Math.random() * 25000000000), - netIncome: Math.floor(Math.random() * 2500000000), - })), - source: 'yahoo-finance', - }; - - await new Promise(resolve => setTimeout(resolve, 300 + Math.random() * 200)); - - return financials; - }, - earnings: async (payload: { symbol: string; period?: 'annual' | 'quarterly' }) => { - const { getLogger } = await import('@stock-bot/logger'); - const logger = getLogger('yahoo-provider'); - - logger.info('Fetching earnings from Yahoo Finance', { - symbol: payload.symbol, - period: payload.period || 'quarterly', - }); - - // Generate mock earnings data - const earnings = { - symbol: payload.symbol, - period: payload.period || 'quarterly', - earnings: Array.from({ length: 8 }, (_, i) => ({ - quarter: `Q${(i % 4) + 1} ${2024 - Math.floor(i / 4)}`, - epsEstimate: Math.random() * 5, - epsActual: Math.random() * 5, - revenueEstimate: Math.floor(Math.random() * 50000000000), - revenueActual: Math.floor(Math.random() * 50000000000), - surprise: (Math.random() - 0.5) * 2, - })), - source: 'yahoo-finance', - }; - - await new Promise(resolve => setTimeout(resolve, 250 + Math.random() * 150)); - - return earnings; - }, - recommendations: async (payload: { symbol: string }) => { - const { getLogger } = await import('@stock-bot/logger'); - const logger = getLogger('yahoo-provider'); - - logger.info('Fetching recommendations from Yahoo Finance', { symbol: payload.symbol }); - - // Generate mock recommendations - const recommendations = { - symbol: payload.symbol, - current: { - strongBuy: Math.floor(Math.random() * 10), - buy: Math.floor(Math.random() * 15), - hold: Math.floor(Math.random() * 20), - sell: Math.floor(Math.random() * 5), - strongSell: Math.floor(Math.random() * 3), - }, - trend: Array.from({ length: 4 }, (_, i) => ({ - period: `${i}m`, - strongBuy: Math.floor(Math.random() * 10), - buy: Math.floor(Math.random() * 15), - hold: Math.floor(Math.random() * 20), - sell: Math.floor(Math.random() * 5), - strongSell: Math.floor(Math.random() * 3), - })), - source: 'yahoo-finance', - }; - - await new Promise(resolve => setTimeout(resolve, 180 + Math.random() * 120)); - return recommendations; - }, - }, - - scheduledJobs: [ - // { - // type: 'yahoo-market-refresh', - // operation: 'live-data', - // payload: { symbol: 'AAPL' }, - // cronPattern: '*/1 * * * *', // Every minute - // priority: 8, - // description: 'Refresh Apple stock price from Yahoo Finance' - // }, - // { - // type: 'yahoo-sp500-update', - // operation: 'live-data', - // payload: { symbol: 'SPY' }, - // cronPattern: '*/2 * * * *', // Every 2 minutes - // priority: 9, - // description: 'Update S&P 500 ETF price' - // }, - // { - // type: 'yahoo-earnings-check', - // operation: 'earnings', - // payload: { symbol: 'AAPL' }, - // cronPattern: '0 16 * * 1-5', // Weekdays at 4 PM (market close) - // priority: 6, - // description: 'Check earnings data for Apple' - // } - ], -}; diff --git a/apps/data-service/src/utils/symbol-search.util.ts b/apps/data-service/src/utils/symbol-search.util.ts new file mode 100644 index 0000000..a84e68f --- /dev/null +++ b/apps/data-service/src/utils/symbol-search.util.ts @@ -0,0 +1,109 @@ +import { getLogger } from '@stock-bot/logger'; +import { sleep } from '@stock-bot/utils'; + +const logger = getLogger('symbol-search-util'); + +export interface SearchFunction { + (query: string): Promise; +} + +export class SymbolSearchUtil { + private threshold: number; + private searchFunction: SearchFunction; + private maxDepth: number; + private delay: number; + + constructor( + searchFunction: SearchFunction, + threshold: number = 50, + maxDepth: number = 4, + delay: number = 100 + ) { + this.searchFunction = searchFunction; + this.threshold = threshold; + this.maxDepth = maxDepth; + this.delay = delay; + } + + async searchAllSymbols(): Promise { + logger.info('Starting comprehensive symbol search...'); + const allSymbols: string[] = []; + + // Start with single letters A-Z + for (let i = 0; i < 26; i++) { + const singleLetter = String.fromCharCode(65 + i); + + try { + const symbols = await this.searchRecursive(singleLetter, 1); + allSymbols.push(...symbols); + + // Add delay between top-level searches + if (this.delay > 0) { + await sleep(this.delay); + } + } catch (error) { + logger.error(`Failed to search for "${singleLetter}":`, error); + // Continue with next letter + } + } + + // Remove duplicates + const uniqueSymbols = [...new Set(allSymbols)]; + logger.info(`Symbol search completed. Found ${uniqueSymbols.length} unique symbols`); + return uniqueSymbols; + } + + private async searchRecursive(prefix: string, depth: number): Promise { + try { + const symbols = await this.searchFunction(prefix); + + logger.debug(`Query "${prefix}" returned ${symbols.length} symbols`); + + // If we're at max depth or results are under threshold, return the symbols + if (depth >= this.maxDepth || symbols.length < this.threshold) { + logger.info(`Added ${symbols.length} symbols from query: ${prefix}`); + return symbols; + } + + // If we have too many results, go deeper + logger.info( + `Query "${prefix}" returned ${symbols.length} results (>= ${this.threshold}), going deeper...` + ); + const allSymbols: string[] = []; + + for (let i = 0; i < 26; i++) { + const nextQuery = prefix + String.fromCharCode(65 + i); + + try { + const deeperSymbols = await this.searchRecursive(nextQuery, depth + 1); + allSymbols.push(...deeperSymbols); + + // Add delay between recursive calls + if (this.delay > 0 && depth < 3) { + // Only delay for first few levels + await sleep(this.delay); + } + } catch (error) { + logger.error(`Failed recursive search for "${nextQuery}":`, error); + // Continue with next combination + } + } + + return allSymbols; + } catch (error) { + logger.error(`Error in recursive search for "${prefix}":`, error); + return []; + } + } + + // Static method for one-off searches + static async search( + searchFunction: SearchFunction, + threshold: number = 50, + maxDepth: number = 4, + delay: number = 100 + ): Promise { + const util = new SymbolSearchUtil(searchFunction, threshold, maxDepth, delay); + return util.searchAllSymbols(); + } +} diff --git a/bun.lock b/bun.lock index b155d09..a2c89ee 100644 --- a/bun.lock +++ b/bun.lock @@ -54,6 +54,8 @@ "@stock-bot/queue": "*", "@stock-bot/shutdown": "*", "@stock-bot/types": "*", + "chromium-bidi": "^5.3.1", + "electron": "^36.4.0", "hono": "^4.0.0", "p-limit": "^6.2.0", "ws": "^8.0.0", @@ -505,6 +507,8 @@ "@balena/dockerignore": ["@balena/dockerignore@1.0.2", "", {}, "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="], + "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], "@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], @@ -743,6 +747,8 @@ "@types/bun": ["@types/bun@1.2.15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="], + "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], + "@types/cookiejar": ["@types/cookiejar@2.1.5", "", {}, "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q=="], "@types/docker-modem": ["@types/docker-modem@3.0.6", "", { "dependencies": { "@types/node": "*", "@types/ssh2": "*" } }, "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg=="], @@ -759,6 +765,8 @@ "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + "@types/keyv": ["@types/keyv@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg=="], + "@types/methods": ["@types/methods@1.1.4", "", {}, "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ=="], "@types/mongodb": ["@types/mongodb@4.0.7", "", { "dependencies": { "mongodb": "*" } }, "sha512-lPUYPpzA43baXqnd36cZ9xxorprybxXDzteVKCPAdp14ppHtFJHnXYvNpmBvtMUTb5fKXVv6sVbzo1LHkWhJlw=="], @@ -781,6 +789,8 @@ "@types/react-window": ["@types/react-window@1.8.8", "", { "dependencies": { "@types/react": "*" } }, "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q=="], + "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="], + "@types/semver": ["@types/semver@7.7.0", "", {}, "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA=="], "@types/ssh2": ["@types/ssh2@0.5.52", "", { "dependencies": { "@types/node": "*", "@types/ssh2-streams": "*" } }, "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg=="], @@ -799,6 +809,8 @@ "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], + "@types/yup": ["@types/yup@0.32.0", "", { "dependencies": { "yup": "*" } }, "sha512-Gr2lllWTDxGVYHgWfL8szjdedERpNgm44L9BDL2cmcHG7Bfd6taEpiW3ayMFLaYvlJr/6bFXDJdh6L406AGlFg=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.34.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/type-utils": "8.34.0", "@typescript-eslint/utils": "8.34.0", "@typescript-eslint/visitor-keys": "8.34.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.34.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w=="], @@ -913,6 +925,8 @@ "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + "boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="], + "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -957,10 +971,14 @@ "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + "chromium-bidi": ["chromium-bidi@5.3.1", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-fbkgn0/m6RIRknVEez+QOYuvukUomBC0XnS8fgdbl9FeunjR3vUvPN6iYrbzXIuaJXYOwGU8FZgOTDzBImGvLw=="], + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + "clone-response": ["clone-response@1.0.3", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA=="], + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], @@ -1033,6 +1051,10 @@ "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], + "detect-node": ["detect-node@2.1.0", "", {}, "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="], + + "devtools-protocol": ["devtools-protocol@0.0.1473885", "", {}, "sha512-J4UUrUc6r5VZXT7xuPEoHpF+vd1MKBtlAUjPnjXZtt1HgG1ccgUzdMc20COqGtWoSKwk/W9DwJ/GUMs1GpnqnA=="], + "dezalgo": ["dezalgo@1.0.4", "", { "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig=="], "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], @@ -1057,6 +1079,8 @@ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + "electron": ["electron@36.4.0", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^22.7.7", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-LLOOZEuW5oqvnjC7HBQhIqjIIJAZCIFjQxltQGLfEC7XFsBoZgQ3u3iFj+Kzw68Xj97u1n57Jdt7P98qLvUibQ=="], + "electron-to-chromium": ["electron-to-chromium@1.5.166", "", {}, "sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -1065,6 +1089,8 @@ "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + "es-abstract": ["es-abstract@1.24.0", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], @@ -1081,6 +1107,8 @@ "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="], + "esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -1129,6 +1157,8 @@ "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], + "fast-copy": ["fast-copy@3.0.2", "", {}, "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -1147,6 +1177,8 @@ "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], @@ -1175,6 +1207,8 @@ "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + "fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], @@ -1197,7 +1231,7 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], + "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], @@ -1205,6 +1239,8 @@ "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + "global-agent": ["global-agent@3.0.0", "", { "dependencies": { "boolean": "^3.0.1", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", "semver": "^7.3.2", "serialize-error": "^7.0.1" } }, "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q=="], + "globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], @@ -1351,8 +1387,12 @@ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + "jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="], "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], @@ -1393,6 +1433,8 @@ "make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="], + "matcher": ["matcher@3.0.0", "", { "dependencies": { "escape-string-regexp": "^4.0.0" } }, "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="], @@ -1419,6 +1461,8 @@ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], + "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], @@ -1601,6 +1645,8 @@ "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="], + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], @@ -1687,6 +1733,8 @@ "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + "roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="], + "rollup": ["rollup@3.29.5", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -1711,6 +1759,10 @@ "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], + + "serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="], + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], @@ -1789,6 +1841,8 @@ "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], + "sumchecker": ["sumchecker@3.0.1", "", { "dependencies": { "debug": "^4.1.0" } }, "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="], + "superagent": ["superagent@8.1.2", "", { "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.4", "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", "formidable": "^2.1.2", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0", "semver": "^7.3.8" } }, "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA=="], "supertest": ["supertest@6.3.4", "", { "dependencies": { "methods": "^1.1.2", "superagent": "^8.1.2" } }, "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw=="], @@ -1871,6 +1925,8 @@ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], @@ -1927,6 +1983,8 @@ "zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", "readable-stream": "^4.0.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="], + "zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="], + "zone.js": ["zone.js@0.15.1", "", {}, "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w=="], "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], @@ -1939,6 +1997,10 @@ "@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], + "@electron/get/got": ["got@11.8.6", "", { "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", "http2-wrapper": "^1.0.0-beta.5.2", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" } }, "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g=="], + + "@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], @@ -2033,12 +2095,16 @@ "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "cacheable-request/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], + "chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], + "compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], "decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], @@ -2069,6 +2135,8 @@ "eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@1.3.0", "", {}, "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="], + "extract-zip/yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], @@ -2101,6 +2169,8 @@ "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "serialize-error/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="], + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], "tailwindcss/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], @@ -2119,6 +2189,22 @@ "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "@electron/get/got/@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], + + "@electron/get/got/@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="], + + "@electron/get/got/cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="], + + "@electron/get/got/cacheable-request": ["cacheable-request@7.0.4", "", { "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", "normalize-url": "^6.0.1", "responselike": "^2.0.0" } }, "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg=="], + + "@electron/get/got/http2-wrapper": ["http2-wrapper@1.0.3", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg=="], + + "@electron/get/got/lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="], + + "@electron/get/got/p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], + + "@electron/get/got/responselike": ["responselike@2.0.1", "", { "dependencies": { "lowercase-keys": "^2.0.0" } }, "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], @@ -2343,6 +2429,8 @@ "dockerode/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + "extract-zip/yauzl/buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], + "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], "lazystream/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], @@ -2365,6 +2453,8 @@ "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + "@electron/get/got/cacheable-request/normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="], + "@stock-bot/config/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], "@stock-bot/config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" } }, "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ=="], diff --git a/libs/queue/src/queue-manager.ts b/libs/queue/src/queue-manager.ts index 6b8f26f..5174490 100644 --- a/libs/queue/src/queue-manager.ts +++ b/libs/queue/src/queue-manager.ts @@ -176,6 +176,7 @@ export class QueueManager { tz: 'UTC', immediately: job.immediately || false, }, + delay: job.delay || 0, removeOnComplete: 1, removeOnFail: 1, attempts: 2, diff --git a/libs/queue/src/types.ts b/libs/queue/src/types.ts index f27b961..ba8207f 100644 --- a/libs/queue/src/types.ts +++ b/libs/queue/src/types.ts @@ -66,6 +66,7 @@ export interface ScheduledJob { priority?: number; description?: string; immediately?: boolean; + delay?: number; } export interface ProviderConfig { diff --git a/libs/utils/src/common.ts b/libs/utils/src/common.ts index 26a47df..afb531d 100644 --- a/libs/utils/src/common.ts +++ b/libs/utils/src/common.ts @@ -5,3 +5,7 @@ export function createProxyUrl(proxy: any): string { } return `${protocol}://${host}:${port}`; } + +export function sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +}