From 263e9513b7665cbb7ebd3b39f7bab60723209c8c Mon Sep 17 00:00:00 2001 From: Boki Date: Tue, 17 Jun 2025 23:19:12 -0400 Subject: [PATCH] added new exchanges system --- .vscode/mcp.json | 21 + CLAUDE.md | 19 + apps/data-sync-service/README.md | 184 ++++ apps/data-sync-service/package.json | 25 + apps/data-sync-service/src/index.ts | 263 ++++++ .../src/services/enhanced-sync-manager.ts | 798 ++++++++++++++++++ apps/data-sync-service/src/services/index.ts | 2 + .../src/services/sync-manager.ts | 306 +++++++ apps/data-sync-service/tsconfig.json | 22 + apps/data-sync-service/turbo.json | 3 + apps/web-api/package.json | 25 + apps/web-api/src/index.ts | 130 +++ apps/web-api/src/routes/exchange.routes.ts | 688 +++++++++++++++ apps/web-api/src/routes/health.routes.ts | 69 ++ apps/web-api/src/routes/index.ts | 5 + apps/web-api/tsconfig.json | 9 + apps/web-api/turbo.json | 13 + apps/{web => web-app}/.env.example | 0 apps/{web => web-app}/README.md | 0 apps/{web => web-app}/eslint.config.js | 0 apps/{web => web-app}/index.html | 0 apps/{web => web-app}/package.json | 2 +- apps/{web => web-app}/postcss.config.js | 0 apps/{web => web-app}/public/vite.svg | 0 apps/{web => web-app}/src/App.tsx | 0 apps/{web => web-app}/src/app/App.tsx | 0 apps/{web => web-app}/src/app/index.ts | 0 apps/{web => web-app}/src/components/index.ts | 0 .../src/components/layout/Header.tsx | 0 .../src/components/layout/Layout.tsx | 0 .../src/components/layout/Sidebar.tsx | 0 .../src/components/layout/index.ts | 0 .../src/components/ui/Card.tsx | 0 .../src/components/ui/DataTable/DataTable.tsx | 0 .../src/components/ui/DataTable/index.ts | 0 .../src/components/ui/DataTable/types.ts | 0 .../src/components/ui/StatCard.tsx | 0 apps/web-app/src/components/ui/button.tsx | 38 + apps/web-app/src/components/ui/dialog.tsx | 59 ++ .../src/components/ui/index.ts | 2 + .../src/features/dashboard/DashboardPage.tsx | 0 .../components/DashboardActivity.tsx | 0 .../dashboard/components/DashboardStats.tsx | 0 .../dashboard/components/PortfolioTable.tsx | 0 .../features/dashboard/components/index.ts | 0 .../src/features/dashboard/index.ts | 0 .../src/features/exchanges/ExchangesPage.tsx | 0 .../components/AddProviderMappingDialog.tsx | 251 ++++++ .../exchanges/components/AddSourceDialog.tsx | 0 .../exchanges/components/ExchangesTable.tsx | 424 ++++++++++ .../features/exchanges/components/index.ts | 1 + .../src/features/exchanges/hooks/index.ts | 0 .../features/exchanges/hooks/useExchanges.ts | 269 ++++++ .../src/features/exchanges/index.ts | 0 .../src/features/exchanges/types/index.ts | 89 ++ apps/{web => web-app}/src/index.css | 0 apps/{web => web-app}/src/lib/constants.ts | 0 .../src/lib/constants/index.ts | 0 .../src/lib/constants/navigation.ts | 0 apps/{web => web-app}/src/lib/utils.ts | 0 apps/{web => web-app}/src/lib/utils/cn.ts | 0 apps/{web => web-app}/src/lib/utils/index.ts | 0 apps/{web => web-app}/src/main.tsx | 0 apps/{web => web-app}/tailwind.config.js | 0 apps/{web => web-app}/tsconfig.json | 0 apps/{web => web-app}/tsconfig.node.json | 0 apps/{web => web-app}/turbo.json | 0 apps/{web => web-app}/vite.config.ts | 12 +- .../exchanges/components/ExchangesTable.tsx | 283 ------- .../features/exchanges/hooks/useExchanges.ts | 155 ---- .../web/src/features/exchanges/types/index.ts | 46 - bun.lock | 706 ++++++++++++++-- database/postgres/init/01-init-database.sql | 13 + database/postgres/init/01-init-schemas.sql | 20 - database/postgres/init/02-master-schema.sql | 63 ++ database/postgres/init/02-trading-tables.sql | 93 -- database/postgres/init/03-initial-data.sql | 23 + .../postgres/init/03-strategy-risk-tables.sql | 105 --- database/postgres/init/04-audit-tables.sql | 59 -- .../04-provider-exchange-mapping-schema.sql | 73 ++ database/postgres/init/05-initial-data.sql | 55 -- database/postgres/providers/01-ib-simple.sql | 0 database/postgres/providers/01-ib.sql | 51 -- database/postgres/providers/README.md | 104 --- database/postgres/scripts/README.md | 56 -- database/postgres/scripts/ib.ts | 0 database/postgres/scripts/init.ts | 41 - .../scripts/populate-ib-exchanges-simple.ts | 0 .../postgres/scripts/populate-ib-exchanges.ts | 0 database/postgres/scripts/setup-ib-fast.ts | 0 .../scripts/setup-ib-schema-simple.ts | 0 database/postgres/scripts/setup-ib-schema.ts | 0 database/postgres/scripts/setup-ib.ts | 366 -------- database/postgres/scripts/setup.ts | 0 libs/http/src/adapters/axios-adapter.ts | 6 +- libs/postgres-client/src/index.ts | 7 +- package.json | 2 + scripts/setup-mcp.sh | 83 ++ 98 files changed, 4643 insertions(+), 1496 deletions(-) create mode 100644 .vscode/mcp.json create mode 100644 apps/data-sync-service/README.md create mode 100644 apps/data-sync-service/package.json create mode 100644 apps/data-sync-service/src/index.ts create mode 100644 apps/data-sync-service/src/services/enhanced-sync-manager.ts create mode 100644 apps/data-sync-service/src/services/index.ts create mode 100644 apps/data-sync-service/src/services/sync-manager.ts create mode 100644 apps/data-sync-service/tsconfig.json create mode 100644 apps/data-sync-service/turbo.json create mode 100644 apps/web-api/package.json create mode 100644 apps/web-api/src/index.ts create mode 100644 apps/web-api/src/routes/exchange.routes.ts create mode 100644 apps/web-api/src/routes/health.routes.ts create mode 100644 apps/web-api/src/routes/index.ts create mode 100644 apps/web-api/tsconfig.json create mode 100644 apps/web-api/turbo.json rename apps/{web => web-app}/.env.example (100%) rename apps/{web => web-app}/README.md (100%) rename apps/{web => web-app}/eslint.config.js (100%) rename apps/{web => web-app}/index.html (100%) rename apps/{web => web-app}/package.json (97%) rename apps/{web => web-app}/postcss.config.js (100%) rename apps/{web => web-app}/public/vite.svg (100%) rename apps/{web => web-app}/src/App.tsx (100%) rename apps/{web => web-app}/src/app/App.tsx (100%) rename apps/{web => web-app}/src/app/index.ts (100%) rename apps/{web => web-app}/src/components/index.ts (100%) rename apps/{web => web-app}/src/components/layout/Header.tsx (100%) rename apps/{web => web-app}/src/components/layout/Layout.tsx (100%) rename apps/{web => web-app}/src/components/layout/Sidebar.tsx (100%) rename apps/{web => web-app}/src/components/layout/index.ts (100%) rename apps/{web => web-app}/src/components/ui/Card.tsx (100%) rename apps/{web => web-app}/src/components/ui/DataTable/DataTable.tsx (100%) rename apps/{web => web-app}/src/components/ui/DataTable/index.ts (100%) rename apps/{web => web-app}/src/components/ui/DataTable/types.ts (100%) rename apps/{web => web-app}/src/components/ui/StatCard.tsx (100%) create mode 100644 apps/web-app/src/components/ui/button.tsx create mode 100644 apps/web-app/src/components/ui/dialog.tsx rename apps/{web => web-app}/src/components/ui/index.ts (54%) rename apps/{web => web-app}/src/features/dashboard/DashboardPage.tsx (100%) rename apps/{web => web-app}/src/features/dashboard/components/DashboardActivity.tsx (100%) rename apps/{web => web-app}/src/features/dashboard/components/DashboardStats.tsx (100%) rename apps/{web => web-app}/src/features/dashboard/components/PortfolioTable.tsx (100%) rename apps/{web => web-app}/src/features/dashboard/components/index.ts (100%) rename apps/{web => web-app}/src/features/dashboard/index.ts (100%) rename apps/{web => web-app}/src/features/exchanges/ExchangesPage.tsx (100%) create mode 100644 apps/web-app/src/features/exchanges/components/AddProviderMappingDialog.tsx rename apps/{web => web-app}/src/features/exchanges/components/AddSourceDialog.tsx (100%) create mode 100644 apps/web-app/src/features/exchanges/components/ExchangesTable.tsx rename apps/{web => web-app}/src/features/exchanges/components/index.ts (59%) rename apps/{web => web-app}/src/features/exchanges/hooks/index.ts (100%) create mode 100644 apps/web-app/src/features/exchanges/hooks/useExchanges.ts rename apps/{web => web-app}/src/features/exchanges/index.ts (100%) create mode 100644 apps/web-app/src/features/exchanges/types/index.ts rename apps/{web => web-app}/src/index.css (100%) rename apps/{web => web-app}/src/lib/constants.ts (100%) rename apps/{web => web-app}/src/lib/constants/index.ts (100%) rename apps/{web => web-app}/src/lib/constants/navigation.ts (100%) rename apps/{web => web-app}/src/lib/utils.ts (100%) rename apps/{web => web-app}/src/lib/utils/cn.ts (100%) rename apps/{web => web-app}/src/lib/utils/index.ts (100%) rename apps/{web => web-app}/src/main.tsx (100%) rename apps/{web => web-app}/tailwind.config.js (100%) rename apps/{web => web-app}/tsconfig.json (100%) rename apps/{web => web-app}/tsconfig.node.json (100%) rename apps/{web => web-app}/turbo.json (100%) rename apps/{web => web-app}/vite.config.ts (58%) delete mode 100644 apps/web/src/features/exchanges/components/ExchangesTable.tsx delete mode 100644 apps/web/src/features/exchanges/hooks/useExchanges.ts delete mode 100644 apps/web/src/features/exchanges/types/index.ts create mode 100644 database/postgres/init/01-init-database.sql delete mode 100644 database/postgres/init/01-init-schemas.sql create mode 100644 database/postgres/init/02-master-schema.sql delete mode 100644 database/postgres/init/02-trading-tables.sql create mode 100644 database/postgres/init/03-initial-data.sql delete mode 100644 database/postgres/init/03-strategy-risk-tables.sql delete mode 100644 database/postgres/init/04-audit-tables.sql create mode 100644 database/postgres/init/04-provider-exchange-mapping-schema.sql delete mode 100644 database/postgres/init/05-initial-data.sql delete mode 100644 database/postgres/providers/01-ib-simple.sql delete mode 100644 database/postgres/providers/01-ib.sql delete mode 100644 database/postgres/providers/README.md delete mode 100644 database/postgres/scripts/README.md delete mode 100644 database/postgres/scripts/ib.ts delete mode 100644 database/postgres/scripts/init.ts delete mode 100644 database/postgres/scripts/populate-ib-exchanges-simple.ts delete mode 100644 database/postgres/scripts/populate-ib-exchanges.ts delete mode 100644 database/postgres/scripts/setup-ib-fast.ts delete mode 100644 database/postgres/scripts/setup-ib-schema-simple.ts delete mode 100644 database/postgres/scripts/setup-ib-schema.ts delete mode 100644 database/postgres/scripts/setup-ib.ts delete mode 100644 database/postgres/scripts/setup.ts create mode 100755 scripts/setup-mcp.sh diff --git a/.vscode/mcp.json b/.vscode/mcp.json new file mode 100644 index 0000000..c77541f --- /dev/null +++ b/.vscode/mcp.json @@ -0,0 +1,21 @@ +{ + "mcpServers": { + "postgres": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-postgres", + "postgresql://trading_user:trading_pass_dev@localhost:5432/trading_bot" + ] + }, + "mongodb": { + "command": "npx", + "args": [ + "-y", + "mongodb-mcp-server", + "--connectionString", + "mongodb://trading_admin:trading_mongo_dev@localhost:27017/stock?authSource=admin" + ] + } + } +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index f3ee03a..1e77f7e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -111,6 +111,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - `scripts/build-all.sh` - Production build with cleanup - `scripts/docker.sh` - Docker management - `scripts/format.sh` - Code formatting +- `scripts/setup-mcp.sh` - Setup Model Context Protocol servers for database access **Documentation**: - `SIMPLIFIED-ARCHITECTURE.md` - Detailed architecture overview @@ -135,6 +136,24 @@ Focus on data quality and provider fault tolerance before advancing to strategy - Prettier for code formatting - All services should have health check endpoints +## Model Context Protocol (MCP) Setup + +**MCP Database Servers** are configured in `.vscode/mcp.json` for direct database access: + +- **PostgreSQL MCP Server**: Provides read-only access to PostgreSQL database + - Connection: `postgresql://trading_user:trading_pass_dev@localhost:5432/trading_bot` + - Package: `@modelcontextprotocol/server-postgres` + +- **MongoDB MCP Server**: Official MongoDB team server for database and Atlas interaction + - Connection: `mongodb://trading_admin:trading_mongo_dev@localhost:27017/stock?authSource=admin` + - Package: `mongodb-mcp-server` (official MongoDB JavaScript team package) + +**Setup Commands**: +- `./scripts/setup-mcp.sh` - Setup and test MCP servers +- `bun run infra:up` - Start database infrastructure (required for MCP) + +**Usage**: Once configured, Claude Code can directly query and inspect database schemas and data through natural language commands. + ## Environment Variables Key environment variables (see `.env` example): diff --git a/apps/data-sync-service/README.md b/apps/data-sync-service/README.md new file mode 100644 index 0000000..1c12102 --- /dev/null +++ b/apps/data-sync-service/README.md @@ -0,0 +1,184 @@ +# Data Sync Service + +The Data Sync Service handles synchronization of raw MongoDB data to PostgreSQL master records, providing a unified data layer for the stock-bot application. + +## Features + +### Original Sync Manager +- Basic QM (QuoteMedia) symbol and exchange synchronization +- Simple static exchange mapping +- Manual sync triggers via REST API + +### Enhanced Sync Manager ✨ NEW +- **Multi-provider support**: Syncs from EOD, Interactive Brokers, and QuoteMedia +- **Comprehensive exchange handling**: Leverages all 4 MongoDB exchange collections +- **Intelligent exchange mapping**: Dynamic mapping with fallback logic +- **Transaction safety**: Full ACID compliance with rollback on errors +- **Performance optimization**: Exchange caching for faster lookups +- **Enhanced error handling**: Detailed error tracking and reporting + +## API Endpoints + +### Health Check +- `GET /health` - Service health status + +### Original Sync Operations +- `POST /sync/symbols` - Sync QM symbols to PostgreSQL +- `POST /sync/exchanges` - Sync QM exchanges to PostgreSQL +- `GET /sync/status` - Get basic sync status + +### Enhanced Sync Operations ✨ NEW +- `POST /sync/exchanges/all?clear=true` - Comprehensive exchange sync from all providers (clear=true removes dummy data first) +- `POST /sync/symbols/:provider?clear=true` - Sync symbols from specific provider (qm, eod, ib) +- `POST /sync/clear` - Clear all PostgreSQL data (exchanges, symbols, mappings) +- `GET /sync/status/enhanced` - Get detailed sync status +- `GET /sync/stats/exchanges` - Get exchange statistics + +## Data Sources + +### MongoDB Collections +1. **exchanges** (34 records) - Unified exchange reference +2. **eodExchanges** (78 records) - EOD provider with currency/MIC data +3. **ibExchanges** (214 records) - Interactive Brokers with asset types +4. **qmExchanges** (25 records) - QuoteMedia exchanges + +### PostgreSQL Tables +1. **master_exchanges** - Unified exchange master data +2. **master_symbols** - Symbol master records +3. **provider_symbol_mappings** - Multi-provider symbol mappings +4. **sync_status** - Synchronization tracking + +## Key Improvements + +### 1. Multi-Provider Exchange Sync +Instead of only syncing QM exchanges, the enhanced sync manager: +- Syncs from unified `exchanges` collection first (primary source) +- Enriches with EOD exchanges (comprehensive global data with currencies) +- Adds IB exchanges for additional coverage (214 exchanges vs 25 in QM) + +### 2. Intelligent Exchange Mapping +Replaces hard-coded mapping with dynamic resolution: +```typescript +// Before: Static mapping +const exchangeMap = { 'NASDAQ': 'NASDAQ', 'NYSE': 'NYSE' }; + +// After: Dynamic mapping with variations +const codeMap = { + 'NASDAQ': 'NASDAQ', 'NAS': 'NASDAQ', + 'NYSE': 'NYSE', 'NYQ': 'NYSE', + 'LSE': 'LSE', 'LON': 'LSE', 'LN': 'LSE', + 'US': 'NYSE' // EOD uses 'US' for US markets +}; +``` + +### 3. Transaction Safety +All sync operations use database transactions: +- `BEGIN` transaction at start +- `COMMIT` on success +- `ROLLBACK` on any error +- Ensures data consistency + +### 4. Performance Optimization +- Exchange cache preloaded at startup +- Reduced database queries during symbol processing +- Batch operations where possible + +### 5. Enhanced Error Handling +- Detailed error logging with context +- Separate error counting in sync results +- Graceful handling of missing/invalid data + +## Usage Examples + +### Clear All Data and Start Fresh Exchange Sync +```bash +curl -X POST "http://localhost:3005/sync/exchanges/all?clear=true" +``` + +### Sync Symbols from Specific Provider +```bash +# Sync QuoteMedia symbols (clear existing symbols first) +curl -X POST "http://localhost:3005/sync/symbols/qm?clear=true" + +# Sync EOD symbols +curl -X POST http://localhost:3005/sync/symbols/eod + +# Sync Interactive Brokers symbols +curl -X POST http://localhost:3005/sync/symbols/ib +``` + +### Clear All PostgreSQL Data +```bash +curl -X POST http://localhost:3005/sync/clear +``` + +### Get Enhanced Status +```bash +curl http://localhost:3005/sync/status/enhanced +curl http://localhost:3005/sync/stats/exchanges +``` + +## Configuration + +### Environment Variables +- `DATA_SYNC_SERVICE_PORT` - Service port (default: 3005) +- `NODE_ENV` - Environment mode + +### Database Connections +- **MongoDB**: `mongodb://trading_admin:trading_mongo_dev@localhost:27017/stock?authSource=admin` +- **PostgreSQL**: `postgresql://trading_user:trading_pass_dev@localhost:5432/trading_bot` + +## Development + +### Build and Run +```bash +# Development mode +bun run dev + +# Build +bun run build + +# Production +bun run start +``` + +### Testing +```bash +# Run tests +bun test + +# Start infrastructure +bun run infra:up + +# Test sync operations +curl -X POST http://localhost:3005/sync/exchanges/all +curl -X POST http://localhost:3005/sync/symbols/qm +``` + +## Architecture + +``` +MongoDB Collections PostgreSQL Tables +┌─ exchanges (34) ┐ ┌─ master_exchanges +├─ eodExchanges (78) ├──▶├─ master_symbols +├─ ibExchanges (214) │ ├─ provider_symbol_mappings +└─ qmExchanges (25) ┘ └─ sync_status + │ + ▼ +Enhanced Sync Manager +- Exchange caching +- Dynamic mapping +- Transaction safety +- Multi-provider support +``` + +## Migration Path + +The enhanced sync manager is designed to work alongside the original sync manager: + +1. **Immediate**: Use enhanced exchange sync for better coverage +2. **Phase 1**: Test enhanced symbol sync with each provider +3. **Phase 2**: Replace original sync manager when confident +4. **Phase 3**: Remove original sync manager and endpoints + +Both managers can be used simultaneously during the transition period. \ No newline at end of file diff --git a/apps/data-sync-service/package.json b/apps/data-sync-service/package.json new file mode 100644 index 0000000..8e0bebf --- /dev/null +++ b/apps/data-sync-service/package.json @@ -0,0 +1,25 @@ +{ + "name": "@stock-bot/data-sync-service", + "version": "1.0.0", + "description": "Sync service from MongoDB raw data to PostgreSQL master records", + "main": "dist/index.js", + "type": "module", + "scripts": { + "dev": "bun --watch src/index.ts", + "build": "bun build src/index.ts --outdir dist --target node", + "start": "bun dist/index.js", + "test": "bun test", + "clean": "rm -rf dist" + }, + "dependencies": { + "@stock-bot/config": "*", + "@stock-bot/logger": "*", + "@stock-bot/mongodb-client": "*", + "@stock-bot/postgres-client": "*", + "@stock-bot/shutdown": "*", + "hono": "^4.0.0" + }, + "devDependencies": { + "typescript": "^5.0.0" + } +} diff --git a/apps/data-sync-service/src/index.ts b/apps/data-sync-service/src/index.ts new file mode 100644 index 0000000..e734908 --- /dev/null +++ b/apps/data-sync-service/src/index.ts @@ -0,0 +1,263 @@ +/** + * Data Sync Service - Sync raw MongoDB data to PostgreSQL master records + */ +import { Hono } from 'hono'; +import { cors } from 'hono/cors'; +import { loadEnvVariables } from '@stock-bot/config'; +import { getLogger, shutdownLoggers } from '@stock-bot/logger'; +import { connectMongoDB, disconnectMongoDB } from '@stock-bot/mongodb-client'; +import { connectPostgreSQL, disconnectPostgreSQL } from '@stock-bot/postgres-client'; +import { Shutdown } from '@stock-bot/shutdown'; +import { enhancedSyncManager } from './services/enhanced-sync-manager'; +import { syncManager } from './services/sync-manager'; + +// Load environment variables +loadEnvVariables(); + +const app = new Hono(); + +// Add CORS middleware +app.use( + '*', + cors({ + origin: '*', + allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowHeaders: ['Content-Type', 'Authorization'], + credentials: false, + }) +); + +const logger = getLogger('data-sync-service'); +const PORT = parseInt(process.env.DATA_SYNC_SERVICE_PORT || '3005'); +let server: ReturnType | null = null; + +// Initialize shutdown manager +const shutdown = Shutdown.getInstance({ timeout: 15000 }); + +// Basic health check endpoint +app.get('/health', c => { + return c.json({ + status: 'healthy', + service: 'data-sync-service', + timestamp: new Date().toISOString(), + }); +}); + +// Manual sync trigger endpoints +app.post('/sync/symbols', async c => { + try { + const result = await syncManager.syncQMSymbols(); + return c.json({ success: true, result }); + } catch (error) { + logger.error('Manual symbol sync failed', { error }); + return c.json( + { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + 500 + ); + } +}); + +app.post('/sync/exchanges', async c => { + try { + const result = await syncManager.syncQMExchanges(); + return c.json({ success: true, result }); + } catch (error) { + logger.error('Manual exchange sync failed', { error }); + return c.json( + { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + 500 + ); + } +}); + +// Get sync status +app.get('/sync/status', async c => { + try { + const status = await syncManager.getSyncStatus(); + return c.json(status); + } catch (error) { + logger.error('Failed to get sync status', { error }); + return c.json({ error: error instanceof Error ? error.message : 'Unknown error' }, 500); + } +}); + +// Enhanced sync endpoints +app.post('/sync/exchanges/all', async c => { + try { + const clearFirst = c.req.query('clear') === 'true'; + const result = await enhancedSyncManager.syncAllExchanges(clearFirst); + return c.json({ success: true, result }); + } catch (error) { + logger.error('Enhanced exchange sync failed', { error }); + return c.json( + { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + 500 + ); + } +}); + +app.post('/sync/provider-mappings/qm', async c => { + try { + const result = await enhancedSyncManager.syncQMProviderMappings(); + return c.json({ success: true, result }); + } catch (error) { + logger.error('QM provider mappings sync failed', { error }); + return c.json( + { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + 500 + ); + } +}); + +app.post('/sync/symbols/:provider', async c => { + try { + const provider = c.req.param('provider'); + const clearFirst = c.req.query('clear') === 'true'; + const result = await enhancedSyncManager.syncSymbolsFromProvider(provider, clearFirst); + return c.json({ success: true, result }); + } catch (error) { + logger.error('Enhanced symbol sync failed', { error }); + return c.json( + { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + 500 + ); + } +}); + +// Clear data endpoint +app.post('/sync/clear', async c => { + try { + const result = await enhancedSyncManager.clearPostgreSQLData(); + return c.json({ success: true, result }); + } catch (error) { + logger.error('Clear PostgreSQL data failed', { error }); + return c.json( + { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + 500 + ); + } +}); + +// Enhanced status endpoints +app.get('/sync/status/enhanced', async c => { + try { + const status = await enhancedSyncManager.getSyncStatus(); + return c.json(status); + } catch (error) { + logger.error('Failed to get enhanced sync status', { error }); + return c.json({ error: error instanceof Error ? error.message : 'Unknown error' }, 500); + } +}); + +app.get('/sync/stats/exchanges', async c => { + try { + const stats = await enhancedSyncManager.getExchangeStats(); + return c.json(stats); + } catch (error) { + logger.error('Failed to get exchange stats', { error }); + return c.json({ error: error instanceof Error ? error.message : 'Unknown error' }, 500); + } +}); + +app.get('/sync/stats/provider-mappings', async c => { + try { + const stats = await enhancedSyncManager.getProviderMappingStats(); + return c.json(stats); + } catch (error) { + logger.error('Failed to get provider mapping stats', { error }); + return c.json({ error: error instanceof Error ? error.message : 'Unknown error' }, 500); + } +}); + +// Initialize services +async function initializeServices() { + logger.info('Initializing data sync service...'); + + try { + // Initialize MongoDB client + logger.info('Connecting to MongoDB...'); + await connectMongoDB(); + logger.info('MongoDB connected'); + + // Initialize PostgreSQL client + logger.info('Connecting to PostgreSQL...'); + await connectPostgreSQL(); + logger.info('PostgreSQL connected'); + + // Initialize sync managers + logger.info('Initializing sync managers...'); + await syncManager.initialize(); + await enhancedSyncManager.initialize(); + logger.info('Sync managers initialized'); + + logger.info('All services initialized successfully'); + } catch (error) { + logger.error('Failed to initialize services', { error }); + throw error; + } +} + +// Start server +async function startServer() { + await initializeServices(); + + server = Bun.serve({ + port: PORT, + fetch: app.fetch, + development: process.env.NODE_ENV === 'development', + }); + + logger.info(`Data Sync Service started on port ${PORT}`); +} + +// Register shutdown handlers +shutdown.onShutdown(async () => { + if (server) { + logger.info('Stopping HTTP server...'); + try { + server.stop(); + logger.info('HTTP server stopped'); + } catch (error) { + logger.error('Error stopping HTTP server', { error }); + } + } +}); + +shutdown.onShutdown(async () => { + logger.info('Shutting down sync managers...'); + try { + await syncManager.shutdown(); + await enhancedSyncManager.shutdown(); + logger.info('Sync managers shut down'); + } catch (error) { + logger.error('Error shutting down sync managers', { error }); + } +}); + +shutdown.onShutdown(async () => { + logger.info('Disconnecting from databases...'); + try { + await disconnectMongoDB(); + await disconnectPostgreSQL(); + logger.info('Database connections closed'); + } catch (error) { + logger.error('Error closing database connections', { error }); + } +}); + +shutdown.onShutdown(async () => { + try { + await shutdownLoggers(); + process.stdout.write('Data sync service loggers shut down\n'); + } catch (error) { + process.stderr.write(`Error shutting down loggers: ${error}\n`); + } +}); + +// Start the service +startServer().catch(error => { + logger.error('Failed to start data sync service', { error }); + process.exit(1); +}); + +logger.info('Data sync service startup initiated'); diff --git a/apps/data-sync-service/src/services/enhanced-sync-manager.ts b/apps/data-sync-service/src/services/enhanced-sync-manager.ts new file mode 100644 index 0000000..b7f523c --- /dev/null +++ b/apps/data-sync-service/src/services/enhanced-sync-manager.ts @@ -0,0 +1,798 @@ +/** + * Enhanced Sync Manager - Improved syncing with comprehensive exchange data + */ +import { getLogger } from '@stock-bot/logger'; +import { getMongoDBClient } from '@stock-bot/mongodb-client'; +import { getPostgreSQLClient } from '@stock-bot/postgres-client'; + +const logger = getLogger('enhanced-sync-manager'); + +interface ExchangeMapping { + id: string; + code: string; + name: string; + country: string; + currency: string; +} + +interface SyncResult { + processed: number; + created: number; + updated: number; + skipped: number; + errors: number; +} + +interface SyncStatus { + provider: string; + dataType: string; + lastSyncAt?: Date; + lastSyncCount: number; + syncErrors?: string; +} + +export class EnhancedSyncManager { + private isInitialized = false; + private mongoClient: any; + private postgresClient: any; + private exchangeCache: Map = new Map(); + + async initialize(): Promise { + if (this.isInitialized) { + logger.warn('Enhanced sync manager already initialized'); + return; + } + + try { + this.mongoClient = getMongoDBClient(); + this.postgresClient = getPostgreSQLClient(); + + // Pre-load exchange mappings for performance + await this.loadExchangeCache(); + + this.isInitialized = true; + logger.info('Enhanced sync manager initialized successfully'); + } catch (error) { + logger.error('Failed to initialize enhanced sync manager', { error }); + throw error; + } + } + + /** + * Helper method to get MongoDB database for direct queries + */ + private getMongoDatabase() { + return this.mongoClient.getDatabase(); + } + + async shutdown(): Promise { + if (!this.isInitialized) { + return; + } + + logger.info('Shutting down enhanced sync manager...'); + this.exchangeCache.clear(); + this.isInitialized = false; + logger.info('Enhanced sync manager shut down successfully'); + } + + /** + * Clear all exchange and symbol data from PostgreSQL + */ + async clearPostgreSQLData(): Promise<{ + exchangesCleared: number; + symbolsCleared: number; + mappingsCleared: number; + }> { + if (!this.isInitialized) { + throw new Error('Enhanced sync manager not initialized'); + } + + logger.info('Clearing existing PostgreSQL data...'); + + try { + // Start transaction for atomic operations + await this.postgresClient.query('BEGIN'); + + // Get counts before clearing + const exchangeCountResult = await this.postgresClient.query( + 'SELECT COUNT(*) as count FROM exchanges' + ); + const symbolCountResult = await this.postgresClient.query( + 'SELECT COUNT(*) as count FROM symbols' + ); + const mappingCountResult = await this.postgresClient.query( + 'SELECT COUNT(*) as count FROM provider_mappings' + ); + + const exchangesCleared = parseInt(exchangeCountResult.rows[0].count); + const symbolsCleared = parseInt(symbolCountResult.rows[0].count); + const mappingsCleared = parseInt(mappingCountResult.rows[0].count); + + // Clear data in correct order (respect foreign keys) + await this.postgresClient.query('DELETE FROM provider_mappings'); + await this.postgresClient.query('DELETE FROM symbols'); + await this.postgresClient.query('DELETE FROM exchanges'); + + // Reset sync status + await this.postgresClient.query( + 'UPDATE sync_status SET last_sync_at = NULL, last_sync_count = 0, sync_errors = NULL' + ); + + await this.postgresClient.query('COMMIT'); + + logger.info('PostgreSQL data cleared successfully', { + exchangesCleared, + symbolsCleared, + mappingsCleared, + }); + + return { exchangesCleared, symbolsCleared, mappingsCleared }; + } catch (error) { + await this.postgresClient.query('ROLLBACK'); + logger.error('Failed to clear PostgreSQL data', { error }); + throw error; + } + } + + /** + * Comprehensive exchange sync from all MongoDB providers + */ + async syncAllExchanges(clearFirst: boolean = true): Promise { + if (!this.isInitialized) { + throw new Error('Enhanced sync manager not initialized'); + } + + logger.info('Starting comprehensive exchange sync...', { clearFirst }); + + const result: SyncResult = { + processed: 0, + created: 0, + updated: 0, + skipped: 0, + errors: 0, + }; + + try { + // Clear existing data if requested + if (clearFirst) { + await this.clearPostgreSQLData(); + } + + // Start transaction for atomic operations + await this.postgresClient.query('BEGIN'); + + // 1. Sync from unified exchanges collection (primary source) + const unifiedResult = await this.syncUnifiedExchanges(); + this.mergeResults(result, unifiedResult); + + // 2. Sync from EOD exchanges (comprehensive global data) + const eodResult = await this.syncEODExchanges(); + this.mergeResults(result, eodResult); + + // 3. Sync from IB exchanges (detailed asset information) + const ibResult = await this.syncIBExchanges(); + this.mergeResults(result, ibResult); + + // 4. Update sync status + await this.updateSyncStatus('all', 'exchanges', result.processed); + + await this.postgresClient.query('COMMIT'); + + // Refresh exchange cache with new data + await this.loadExchangeCache(); + + logger.info('Comprehensive exchange sync completed', result); + return result; + } catch (error) { + await this.postgresClient.query('ROLLBACK'); + logger.error('Comprehensive exchange sync failed', { error }); + throw error; + } + } + + /** + * Sync QM provider exchange mappings + */ + async syncQMProviderMappings(): Promise { + if (!this.isInitialized) { + throw new Error('Enhanced sync manager not initialized'); + } + + logger.info('Starting QM provider exchange mappings sync...'); + + const result: SyncResult = { + processed: 0, + created: 0, + updated: 0, + skipped: 0, + errors: 0, + }; + + try { + // Start transaction + await this.postgresClient.query('BEGIN'); + + // Get unique exchange combinations from QM symbols + const db = this.getMongoDatabase(); + const pipeline = [ + { + $group: { + _id: { + exchangeCode: '$exchangeCode', + exchange: '$exchange', + countryCode: '$countryCode', + }, + count: { $sum: 1 }, + sampleExchange: { $first: '$exchange' }, + }, + }, + { + $project: { + exchangeCode: '$_id.exchangeCode', + exchange: '$_id.exchange', + countryCode: '$_id.countryCode', + count: 1, + sampleExchange: 1, + }, + }, + ]; + + const qmExchanges = await db.collection('qmSymbols').aggregate(pipeline).toArray(); + logger.info(`Found ${qmExchanges.length} unique QM exchange combinations`); + + for (const exchange of qmExchanges) { + try { + // Create provider exchange mapping for QM + await this.createProviderExchangeMapping( + 'qm', // provider + exchange.exchangeCode, + exchange.sampleExchange || exchange.exchangeCode, + exchange.countryCode, + exchange.countryCode === 'CA' ? 'CAD' : 'USD', // Simple currency mapping + 0.8 // good confidence for QM data + ); + + result.processed++; + result.created++; + } catch (error) { + logger.error('Failed to process QM exchange mapping', { error, exchange }); + result.errors++; + } + } + + await this.postgresClient.query('COMMIT'); + + logger.info('QM provider exchange mappings sync completed', result); + return result; + } catch (error) { + await this.postgresClient.query('ROLLBACK'); + logger.error('QM provider exchange mappings sync failed', { error }); + throw error; + } + } + + /** + * Enhanced symbol sync with multi-provider mapping + */ + async syncSymbolsFromProvider( + provider: string, + clearFirst: boolean = false + ): Promise { + if (!this.isInitialized) { + throw new Error('Enhanced sync manager not initialized'); + } + + logger.info(`Starting ${provider} symbols sync...`, { clearFirst }); + + const result: SyncResult = { + processed: 0, + created: 0, + updated: 0, + skipped: 0, + errors: 0, + }; + + try { + // Clear existing data if requested (only symbols and mappings, keep exchanges) + if (clearFirst) { + await this.postgresClient.query('BEGIN'); + await this.postgresClient.query('DELETE FROM provider_mappings'); + await this.postgresClient.query('DELETE FROM symbols'); + await this.postgresClient.query('COMMIT'); + logger.info('Cleared existing symbols and mappings before sync'); + } + + // Start transaction + await this.postgresClient.query('BEGIN'); + + let symbols: any[] = []; + + // Get symbols based on provider + const db = this.getMongoDatabase(); + switch (provider.toLowerCase()) { + case 'qm': + symbols = await db.collection('qmSymbols').find({}).toArray(); + break; + case 'eod': + symbols = await db.collection('eodSymbols').find({}).toArray(); + break; + case 'ib': + symbols = await db.collection('ibSymbols').find({}).toArray(); + break; + default: + throw new Error(`Unsupported provider: ${provider}`); + } + + logger.info(`Found ${symbols.length} ${provider} symbols to process`); + result.processed = symbols.length; + + for (const symbol of symbols) { + try { + await this.processSingleSymbol(symbol, provider, result); + } catch (error) { + logger.error('Failed to process symbol', { + error, + symbol: symbol.symbol || symbol.code, + provider, + }); + result.errors++; + } + } + + // Update sync status + await this.updateSyncStatus(provider, 'symbols', result.processed); + + await this.postgresClient.query('COMMIT'); + + logger.info(`${provider} symbols sync completed`, result); + return result; + } catch (error) { + await this.postgresClient.query('ROLLBACK'); + logger.error(`${provider} symbols sync failed`, { error }); + throw error; + } + } + + /** + * Get comprehensive sync status + */ + async getSyncStatus(): Promise { + const query = ` + SELECT provider, data_type as "dataType", last_sync_at as "lastSyncAt", + last_sync_count as "lastSyncCount", sync_errors as "syncErrors" + FROM sync_status + ORDER BY provider, data_type + `; + const result = await this.postgresClient.query(query); + return result.rows; + } + + /** + * Get exchange statistics + */ + async getExchangeStats(): Promise { + const query = ` + SELECT + COUNT(*) as total_exchanges, + COUNT(CASE WHEN active = true THEN 1 END) as active_exchanges, + COUNT(DISTINCT country) as countries, + COUNT(DISTINCT currency) as currencies + FROM exchanges + `; + const result = await this.postgresClient.query(query); + return result.rows[0]; + } + + /** + * Get provider exchange mapping statistics + */ + async getProviderMappingStats(): Promise { + const query = ` + SELECT + provider, + COUNT(*) as total_mappings, + COUNT(CASE WHEN active = true THEN 1 END) as active_mappings, + COUNT(CASE WHEN verified = true THEN 1 END) as verified_mappings, + COUNT(CASE WHEN auto_mapped = true THEN 1 END) as auto_mapped, + AVG(confidence) as avg_confidence + FROM provider_exchange_mappings + GROUP BY provider + ORDER BY provider + `; + const result = await this.postgresClient.query(query); + return result.rows; + } + + // Private helper methods + + private async loadExchangeCache(): Promise { + const query = 'SELECT id, code, name, country, currency FROM exchanges'; + const result = await this.postgresClient.query(query); + + this.exchangeCache.clear(); + for (const exchange of result.rows) { + this.exchangeCache.set(exchange.code.toUpperCase(), exchange); + } + + logger.info(`Loaded ${this.exchangeCache.size} exchanges into cache`); + } + + private async syncUnifiedExchanges(): Promise { + const db = this.getMongoDatabase(); + const exchanges = await db.collection('exchanges').find({}).toArray(); + const result: SyncResult = { processed: 0, created: 0, updated: 0, skipped: 0, errors: 0 }; + + for (const exchange of exchanges) { + try { + // Create provider exchange mapping for unified collection + await this.createProviderExchangeMapping( + 'unified', // provider + exchange.sourceCode || exchange.code, + exchange.sourceName || exchange.name, + exchange.sourceRegion, + null, // currency not in unified + 0.9 // high confidence for unified mappings + ); + + result.processed++; + result.created++; // Count as created mapping + } catch (error) { + logger.error('Failed to process unified exchange', { error, exchange }); + result.errors++; + } + } + + return result; + } + + private async syncEODExchanges(): Promise { + const db = this.getMongoDatabase(); + const exchanges = await db.collection('eodExchanges').find({ active: true }).toArray(); + const result: SyncResult = { processed: 0, created: 0, updated: 0, skipped: 0, errors: 0 }; + + for (const exchange of exchanges) { + try { + // Create provider exchange mapping for EOD + await this.createProviderExchangeMapping( + 'eod', // provider + exchange.Code, + exchange.Name, + exchange.CountryISO2, + exchange.Currency, + 0.95 // very high confidence for EOD data + ); + + result.processed++; + result.created++; // Count as created mapping + } catch (error) { + logger.error('Failed to process EOD exchange', { error, exchange }); + result.errors++; + } + } + + return result; + } + + private async syncIBExchanges(): Promise { + const db = this.getMongoDatabase(); + const exchanges = await db.collection('ibExchanges').find({}).toArray(); + const result: SyncResult = { processed: 0, created: 0, updated: 0, skipped: 0, errors: 0 }; + + for (const exchange of exchanges) { + try { + // Create provider exchange mapping for IB + await this.createProviderExchangeMapping( + 'ib', // provider + exchange.exchange_id, + exchange.name, + exchange.country_code, + 'USD', // IB doesn't specify currency, default to USD + 0.85 // good confidence for IB data + ); + + result.processed++; + result.created++; // Count as created mapping + } catch (error) { + logger.error('Failed to process IB exchange', { error, exchange }); + result.errors++; + } + } + + return result; + } + + /** + * Create or update a provider exchange mapping + * This method intelligently maps provider exchanges to master exchanges + */ + private async createProviderExchangeMapping( + provider: string, + providerExchangeCode: string, + providerExchangeName: string, + countryCode: string | null, + currency: string | null, + confidence: number + ): Promise { + if (!providerExchangeCode) return; + + // Check if mapping already exists + const existingMapping = await this.findProviderExchangeMapping(provider, providerExchangeCode); + if (existingMapping) { + // Don't override existing mappings to preserve manual work + return; + } + + // Find or create master exchange + const masterExchange = await this.findOrCreateMasterExchange( + providerExchangeCode, + providerExchangeName, + countryCode, + currency + ); + + // Create the provider exchange mapping + const query = ` + INSERT INTO provider_exchange_mappings + (provider, provider_exchange_code, provider_exchange_name, master_exchange_id, + country_code, currency, confidence, active, auto_mapped) + VALUES ($1, $2, $3, $4, $5, $6, $7, false, true) + ON CONFLICT (provider, provider_exchange_code) DO NOTHING + `; + + await this.postgresClient.query(query, [ + provider, + providerExchangeCode, + providerExchangeName, + masterExchange.id, + countryCode, + currency, + confidence, + ]); + } + + /** + * Find or create a master exchange based on provider data + */ + private async findOrCreateMasterExchange( + providerCode: string, + providerName: string, + countryCode: string | null, + currency: string | null + ): Promise { + // First, try to find exact match + let masterExchange = await this.findExchangeByCode(providerCode); + + if (masterExchange) { + return masterExchange; + } + + // Try to find by similar codes (basic mapping) + const basicMapping = this.getBasicExchangeMapping(providerCode); + if (basicMapping) { + masterExchange = await this.findExchangeByCode(basicMapping); + if (masterExchange) { + return masterExchange; + } + } + + // Create new master exchange (inactive by default) + const query = ` + INSERT INTO exchanges (code, name, country, currency, active) + VALUES ($1, $2, $3, $4, false) + ON CONFLICT (code) DO UPDATE SET + name = COALESCE(EXCLUDED.name, exchanges.name), + country = COALESCE(EXCLUDED.country, exchanges.country), + currency = COALESCE(EXCLUDED.currency, exchanges.currency) + RETURNING id, code, name, country, currency + `; + + const result = await this.postgresClient.query(query, [ + providerCode, + providerName || providerCode, + countryCode || 'US', + currency || 'USD', + ]); + + const newExchange = result.rows[0]; + + // Update cache + this.exchangeCache.set(newExchange.code.toUpperCase(), newExchange); + + return newExchange; + } + + /** + * Basic exchange code mapping for common cases + */ + private getBasicExchangeMapping(providerCode: string): string | null { + const mappings: Record = { + NYE: 'NYSE', + NAS: 'NASDAQ', + TO: 'TSX', + LN: 'LSE', + LON: 'LSE', + }; + + return mappings[providerCode.toUpperCase()] || null; + } + + private async findProviderExchangeMapping( + provider: string, + providerExchangeCode: string + ): Promise { + const query = + 'SELECT * FROM provider_exchange_mappings WHERE provider = $1 AND provider_exchange_code = $2'; + const result = await this.postgresClient.query(query, [provider, providerExchangeCode]); + return result.rows[0] || null; + } + + private async processSingleSymbol( + symbol: any, + provider: string, + result: SyncResult + ): Promise { + const symbolCode = symbol.symbol || symbol.code; + const exchangeCode = symbol.exchangeCode || symbol.exchange || symbol.exchange_id; + + if (!symbolCode || !exchangeCode) { + result.skipped++; + return; + } + + // Find active provider exchange mapping + const providerMapping = await this.findActiveProviderExchangeMapping(provider, exchangeCode); + + if (!providerMapping) { + result.skipped++; + return; + } + + // Check if symbol exists + const existingSymbol = await this.findSymbolByCodeAndExchange( + symbolCode, + providerMapping.master_exchange_id + ); + + if (existingSymbol) { + await this.updateSymbol(existingSymbol.id, symbol); + await this.upsertProviderMapping(existingSymbol.id, provider, symbol); + result.updated++; + } else { + const newSymbolId = await this.createSymbol(symbol, providerMapping.master_exchange_id); + await this.upsertProviderMapping(newSymbolId, provider, symbol); + result.created++; + } + } + + private async findActiveProviderExchangeMapping( + provider: string, + providerExchangeCode: string + ): Promise { + const query = ` + SELECT pem.*, e.code as master_exchange_code + FROM provider_exchange_mappings pem + JOIN exchanges e ON pem.master_exchange_id = e.id + WHERE pem.provider = $1 AND pem.provider_exchange_code = $2 AND pem.active = true + `; + const result = await this.postgresClient.query(query, [provider, providerExchangeCode]); + return result.rows[0] || null; + } + + private async findExchangeByCode(code: string): Promise { + const query = 'SELECT * FROM exchanges WHERE code = $1'; + const result = await this.postgresClient.query(query, [code]); + return result.rows[0] || null; + } + + private async findSymbolByCodeAndExchange(symbol: string, exchangeId: string): Promise { + const query = 'SELECT * FROM symbols WHERE symbol = $1 AND exchange_id = $2'; + const result = await this.postgresClient.query(query, [symbol, exchangeId]); + return result.rows[0] || null; + } + + private async createSymbol(symbol: any, exchangeId: string): Promise { + const query = ` + INSERT INTO symbols (symbol, exchange_id, company_name, country, currency) + VALUES ($1, $2, $3, $4, $5) + RETURNING id + `; + + const result = await this.postgresClient.query(query, [ + symbol.symbol || symbol.code, + exchangeId, + symbol.companyName || symbol.name || symbol.company_name, + symbol.countryCode || symbol.country_code || 'US', + symbol.currency || 'USD', + ]); + + return result.rows[0].id; + } + + private async updateSymbol(symbolId: string, symbol: any): Promise { + const query = ` + UPDATE symbols + SET company_name = COALESCE($2, company_name), + country = COALESCE($3, country), + currency = COALESCE($4, currency), + updated_at = NOW() + WHERE id = $1 + `; + + await this.postgresClient.query(query, [ + symbolId, + symbol.companyName || symbol.name || symbol.company_name, + symbol.countryCode || symbol.country_code, + symbol.currency, + ]); + } + + private async upsertProviderMapping( + symbolId: string, + provider: string, + symbol: any + ): Promise { + const query = ` + INSERT INTO provider_mappings + (symbol_id, provider, provider_symbol, provider_exchange, last_seen) + VALUES ($1, $2, $3, $4, NOW()) + ON CONFLICT (provider, provider_symbol) + DO UPDATE SET + symbol_id = EXCLUDED.symbol_id, + provider_exchange = EXCLUDED.provider_exchange, + last_seen = NOW() + `; + + await this.postgresClient.query(query, [ + symbolId, + provider, + symbol.qmSearchCode || symbol.symbol || symbol.code, + symbol.exchangeCode || symbol.exchange || symbol.exchange_id, + ]); + } + + private async updateSyncStatus(provider: string, dataType: string, count: number): Promise { + const query = ` + INSERT INTO sync_status (provider, data_type, last_sync_at, last_sync_count, sync_errors) + VALUES ($1, $2, NOW(), $3, NULL) + ON CONFLICT (provider, data_type) + DO UPDATE SET + last_sync_at = NOW(), + last_sync_count = EXCLUDED.last_sync_count, + sync_errors = NULL, + updated_at = NOW() + `; + + await this.postgresClient.query(query, [provider, dataType, count]); + } + + private normalizeCountryCode(countryCode: string): string | null { + if (!countryCode) return null; + + // Map common country variations to ISO 2-letter codes + const countryMap: Record = { + 'United States': 'US', + USA: 'US', + Canada: 'CA', + 'United Kingdom': 'GB', + UK: 'GB', + Germany: 'DE', + Japan: 'JP', + Australia: 'AU', + }; + + const normalized = countryMap[countryCode]; + return normalized || (countryCode.length === 2 ? countryCode.toUpperCase() : null); + } + + private mergeResults(target: SyncResult, source: SyncResult): void { + target.processed += source.processed; + target.created += source.created; + target.updated += source.updated; + target.skipped += source.skipped; + target.errors += source.errors; + } +} + +// Export singleton instance +export const enhancedSyncManager = new EnhancedSyncManager(); diff --git a/apps/data-sync-service/src/services/index.ts b/apps/data-sync-service/src/services/index.ts new file mode 100644 index 0000000..43ab0c3 --- /dev/null +++ b/apps/data-sync-service/src/services/index.ts @@ -0,0 +1,2 @@ +export { syncManager } from './sync-manager'; +export { enhancedSyncManager } from './enhanced-sync-manager'; diff --git a/apps/data-sync-service/src/services/sync-manager.ts b/apps/data-sync-service/src/services/sync-manager.ts new file mode 100644 index 0000000..f1d774c --- /dev/null +++ b/apps/data-sync-service/src/services/sync-manager.ts @@ -0,0 +1,306 @@ +/** + * Sync Manager - Handles syncing raw MongoDB data to PostgreSQL master records + */ +import { getLogger } from '@stock-bot/logger'; +import { getMongoDBClient } from '@stock-bot/mongodb-client'; +import { getPostgreSQLClient } from '@stock-bot/postgres-client'; + +const logger = getLogger('sync-manager'); + +export class SyncManager { + private isInitialized = false; + private mongoClient: any; + private postgresClient: any; + + async initialize(): Promise { + if (this.isInitialized) { + logger.warn('Sync manager already initialized'); + return; + } + + try { + this.mongoClient = getMongoDBClient(); + this.postgresClient = getPostgreSQLClient(); + + this.isInitialized = true; + logger.info('Sync manager initialized successfully'); + } catch (error) { + logger.error('Failed to initialize sync manager', { error }); + throw error; + } + } + + async shutdown(): Promise { + if (!this.isInitialized) { + return; + } + + logger.info('Shutting down sync manager...'); + this.isInitialized = false; + logger.info('Sync manager shut down successfully'); + } + + /** + * Sync QM symbols from MongoDB to PostgreSQL + */ + async syncQMSymbols(): Promise<{ processed: number; created: number; updated: number }> { + if (!this.isInitialized) { + throw new Error('Sync manager not initialized'); + } + + logger.info('Starting QM symbols sync...'); + + try { + // 1. Get all QM symbols from MongoDB + const qmSymbols = await this.mongoClient.find('qmSymbols', {}); + logger.info(`Found ${qmSymbols.length} QM symbols to process`); + + let created = 0; + let updated = 0; + + for (const symbol of qmSymbols) { + try { + // 2. Resolve exchange + const exchangeId = await this.resolveExchange(symbol.exchangeCode || symbol.exchange); + + if (!exchangeId) { + logger.warn('Unknown exchange, skipping symbol', { + symbol: symbol.symbol, + exchange: symbol.exchangeCode || symbol.exchange, + }); + continue; + } + + // 3. Check if symbol exists + const existingSymbol = await this.findSymbol(symbol.symbol, exchangeId); + + if (existingSymbol) { + // Update existing + await this.updateSymbol(existingSymbol.id, symbol); + await this.upsertProviderMapping(existingSymbol.id, 'qm', symbol); + updated++; + } else { + // Create new + const newSymbolId = await this.createSymbol(symbol, exchangeId); + await this.upsertProviderMapping(newSymbolId, 'qm', symbol); + created++; + } + } catch (error) { + logger.error('Failed to process symbol', { error, symbol: symbol.symbol }); + } + } + + // 4. Update sync status + await this.updateSyncStatus('qm', 'symbols', qmSymbols.length); + + const result = { processed: qmSymbols.length, created, updated }; + logger.info('QM symbols sync completed', result); + return result; + } catch (error) { + logger.error('QM symbols sync failed', { error }); + throw error; + } + } + + /** + * Sync QM exchanges from MongoDB to PostgreSQL + */ + async syncQMExchanges(): Promise<{ processed: number; created: number; updated: number }> { + if (!this.isInitialized) { + throw new Error('Sync manager not initialized'); + } + + logger.info('Starting QM exchanges sync...'); + + try { + // 1. Get all QM exchanges from MongoDB + const qmExchanges = await this.mongoClient.find('qmExchanges', {}); + logger.info(`Found ${qmExchanges.length} QM exchanges to process`); + + let created = 0; + let updated = 0; + + for (const exchange of qmExchanges) { + try { + // 2. Check if exchange exists + const existingExchange = await this.findExchange(exchange.exchangeCode); + + if (existingExchange) { + // Update existing + await this.updateExchange(existingExchange.id, exchange); + updated++; + } else { + // Create new + await this.createExchange(exchange); + created++; + } + } catch (error) { + logger.error('Failed to process exchange', { error, exchange: exchange.exchangeCode }); + } + } + + // 3. Update sync status + await this.updateSyncStatus('qm', 'exchanges', qmExchanges.length); + + const result = { processed: qmExchanges.length, created, updated }; + logger.info('QM exchanges sync completed', result); + return result; + } catch (error) { + logger.error('QM exchanges sync failed', { error }); + throw error; + } + } + + /** + * Get sync status for all providers + */ + async getSyncStatus(): Promise { + const query = 'SELECT * FROM sync_status ORDER BY provider, data_type'; + const result = await this.postgresClient.query(query); + return result.rows; + } + + // Helper methods + + private async resolveExchange(exchangeCode: string): Promise { + if (!exchangeCode) return null; + + // Simple mapping - expand this as needed + const exchangeMap: Record = { + NASDAQ: 'NASDAQ', + NYSE: 'NYSE', + TSX: 'TSX', + TSE: 'TSX', // TSE maps to TSX + LSE: 'LSE', + CME: 'CME', + }; + + const normalizedCode = exchangeMap[exchangeCode.toUpperCase()]; + if (!normalizedCode) { + return null; + } + + const query = 'SELECT id FROM exchanges WHERE code = $1'; + const result = await this.postgresClient.query(query, [normalizedCode]); + return result.rows[0]?.id || null; + } + + private async findSymbol(symbol: string, exchangeId: string): Promise { + const query = 'SELECT * FROM symbols WHERE symbol = $1 AND exchange_id = $2'; + const result = await this.postgresClient.query(query, [symbol, exchangeId]); + return result.rows[0] || null; + } + + private async createSymbol(qmSymbol: any, exchangeId: string): Promise { + const query = ` + INSERT INTO symbols (symbol, exchange_id, company_name, country, currency) + VALUES ($1, $2, $3, $4, $5) + RETURNING id + `; + + const result = await this.postgresClient.query(query, [ + qmSymbol.symbol, + exchangeId, + qmSymbol.companyName || qmSymbol.name, + qmSymbol.countryCode || 'US', + qmSymbol.currency || 'USD', + ]); + + return result.rows[0].id; + } + + private async updateSymbol(symbolId: string, qmSymbol: any): Promise { + const query = ` + UPDATE symbols + SET company_name = COALESCE($2, company_name), + country = COALESCE($3, country), + currency = COALESCE($4, currency), + updated_at = NOW() + WHERE id = $1 + `; + + await this.postgresClient.query(query, [ + symbolId, + qmSymbol.companyName || qmSymbol.name, + qmSymbol.countryCode, + qmSymbol.currency, + ]); + } + + private async upsertProviderMapping( + symbolId: string, + provider: string, + qmSymbol: any + ): Promise { + const query = ` + INSERT INTO provider_mappings + (symbol_id, provider, provider_symbol, provider_exchange, last_seen) + VALUES ($1, $2, $3, $4, NOW()) + ON CONFLICT (provider, provider_symbol) + DO UPDATE SET + symbol_id = EXCLUDED.symbol_id, + provider_exchange = EXCLUDED.provider_exchange, + last_seen = NOW() + `; + + await this.postgresClient.query(query, [ + symbolId, + provider, + qmSymbol.qmSearchCode || qmSymbol.symbol, + qmSymbol.exchangeCode || qmSymbol.exchange, + ]); + } + + private async findExchange(exchangeCode: string): Promise { + const query = 'SELECT * FROM exchanges WHERE code = $1'; + const result = await this.postgresClient.query(query, [exchangeCode]); + return result.rows[0] || null; + } + + private async createExchange(qmExchange: any): Promise { + const query = ` + INSERT INTO exchanges (code, name, country, currency) + VALUES ($1, $2, $3, $4) + ON CONFLICT (code) DO NOTHING + `; + + await this.postgresClient.query(query, [ + qmExchange.exchangeCode || qmExchange.exchange, + qmExchange.exchangeShortName || qmExchange.name, + qmExchange.countryCode || 'US', + 'USD', // Default currency, can be improved + ]); + } + + private async updateExchange(exchangeId: string, qmExchange: any): Promise { + const query = ` + UPDATE exchanges + SET name = COALESCE($2, name), + country = COALESCE($3, country), + updated_at = NOW() + WHERE id = $1 + `; + + await this.postgresClient.query(query, [ + exchangeId, + qmExchange.exchangeShortName || qmExchange.name, + qmExchange.countryCode, + ]); + } + + private async updateSyncStatus(provider: string, dataType: string, count: number): Promise { + const query = ` + UPDATE sync_status + SET last_sync_at = NOW(), + last_sync_count = $3, + sync_errors = NULL, + updated_at = NOW() + WHERE provider = $1 AND data_type = $2 + `; + + await this.postgresClient.query(query, [provider, dataType, count]); + } +} + +// Export singleton instance +export const syncManager = new SyncManager(); diff --git a/apps/data-sync-service/tsconfig.json b/apps/data-sync-service/tsconfig.json new file mode 100644 index 0000000..0f7cff9 --- /dev/null +++ b/apps/data-sync-service/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "resolveJsonModule": true, + "allowImportingTsExtensions": false, + "noEmit": false, + "allowSyntheticDefaultImports": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} diff --git a/apps/data-sync-service/turbo.json b/apps/data-sync-service/turbo.json new file mode 100644 index 0000000..3adcb89 --- /dev/null +++ b/apps/data-sync-service/turbo.json @@ -0,0 +1,3 @@ +{ + "extends": ["//"] +} diff --git a/apps/web-api/package.json b/apps/web-api/package.json new file mode 100644 index 0000000..9fa41b1 --- /dev/null +++ b/apps/web-api/package.json @@ -0,0 +1,25 @@ +{ + "name": "@stock-bot/web-api", + "version": "1.0.0", + "description": "REST API service for stock bot web application", + "main": "dist/index.js", + "type": "module", + "scripts": { + "dev": "bun --watch src/index.ts", + "build": "bun build src/index.ts --outdir dist --target node", + "start": "bun dist/index.js", + "test": "bun test", + "clean": "rm -rf dist" + }, + "dependencies": { + "@stock-bot/config": "*", + "@stock-bot/logger": "*", + "@stock-bot/mongodb-client": "*", + "@stock-bot/postgres-client": "*", + "@stock-bot/shutdown": "*", + "hono": "^4.0.0" + }, + "devDependencies": { + "typescript": "^5.0.0" + } +} diff --git a/apps/web-api/src/index.ts b/apps/web-api/src/index.ts new file mode 100644 index 0000000..f54d9a0 --- /dev/null +++ b/apps/web-api/src/index.ts @@ -0,0 +1,130 @@ +/** + * Stock Bot Web API - REST API service for web application + */ +import { Hono } from 'hono'; +import { cors } from 'hono/cors'; +import { loadEnvVariables } from '@stock-bot/config'; +import { getLogger, shutdownLoggers } from '@stock-bot/logger'; +import { connectMongoDB, disconnectMongoDB } from '@stock-bot/mongodb-client'; +import { connectPostgreSQL, disconnectPostgreSQL } from '@stock-bot/postgres-client'; +import { Shutdown } from '@stock-bot/shutdown'; +// Import routes +import { exchangeRoutes } from './routes/exchange.routes'; +import { healthRoutes } from './routes/health.routes'; + +// Load environment variables +loadEnvVariables(); + +const app = new Hono(); + +// Add CORS middleware +app.use( + '*', + cors({ + origin: ['http://localhost:4200', 'http://localhost:3000'], // React dev server ports + allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], + allowHeaders: ['Content-Type', 'Authorization'], + credentials: true, + }) +); + +const logger = getLogger('web-api'); +const PORT = parseInt(process.env.WEB_API_PORT || '4000'); +let server: ReturnType | null = null; + +// Initialize shutdown manager +const shutdown = Shutdown.getInstance({ timeout: 15000 }); + +// Add routes +app.route('/health', healthRoutes); +app.route('/api/exchanges', exchangeRoutes); + +// Basic API info endpoint +app.get('/', c => { + return c.json({ + name: 'Stock Bot Web API', + version: '1.0.0', + status: 'running', + timestamp: new Date().toISOString(), + endpoints: { + health: '/health', + exchanges: '/api/exchanges', + }, + }); +}); + +// Initialize services +async function initializeServices() { + logger.info('Initializing web API service...'); + + try { + // Initialize MongoDB client + logger.info('Connecting to MongoDB...'); + await connectMongoDB(); + logger.info('MongoDB connected'); + + // Initialize PostgreSQL client + logger.info('Connecting to PostgreSQL...'); + await connectPostgreSQL(); + logger.info('PostgreSQL connected'); + + logger.info('All services initialized successfully'); + } catch (error) { + logger.error('Failed to initialize services', { error }); + throw error; + } +} + +// Start server +async function startServer() { + await initializeServices(); + + server = Bun.serve({ + port: PORT, + fetch: app.fetch, + development: process.env.NODE_ENV === 'development', + }); + + logger.info(`Stock Bot Web API started on port ${PORT}`); +} + +// Register shutdown handlers +shutdown.onShutdown(async () => { + if (server) { + logger.info('Stopping HTTP server...'); + try { + server.stop(); + logger.info('HTTP server stopped'); + } catch (error) { + logger.error('Error stopping HTTP server', { error }); + } + } +}); + +shutdown.onShutdown(async () => { + logger.info('Disconnecting from databases...'); + try { + await disconnectMongoDB(); + await disconnectPostgreSQL(); + logger.info('Database connections closed'); + } catch (error) { + logger.error('Error closing database connections', { error }); + } +}); + +shutdown.onShutdown(async () => { + try { + await shutdownLoggers(); + process.stdout.write('Web API loggers shut down\n'); + } catch (error) { + process.stderr.write(`Error shutting down loggers: ${error}\n`); + } +}); + +// Start the service +startServer().catch(error => { + logger.error('Failed to start web API service', { error }); + process.exit(1); +}); + +logger.info('Web API service startup initiated'); diff --git a/apps/web-api/src/routes/exchange.routes.ts b/apps/web-api/src/routes/exchange.routes.ts new file mode 100644 index 0000000..659faec --- /dev/null +++ b/apps/web-api/src/routes/exchange.routes.ts @@ -0,0 +1,688 @@ +/** + * Exchange management routes + */ +import { Hono } from 'hono'; +import { getLogger } from '@stock-bot/logger'; +import { getPostgreSQLClient } from '@stock-bot/postgres-client'; +import { getMongoDBClient } from '@stock-bot/mongodb-client'; + +const logger = getLogger('exchange-routes'); +export const exchangeRoutes = new Hono(); + +// Get all exchanges with provider mapping counts +exchangeRoutes.get('/', async c => { + try { + const postgresClient = getPostgreSQLClient(); + + const query = ` + SELECT + e.id, + e.code, + e.name, + e.country, + e.currency, + e.active, + e.created_at, + e.updated_at, + COUNT(pem.id) as provider_mapping_count, + COUNT(CASE WHEN pem.active = true THEN 1 END) as active_mapping_count, + COUNT(CASE WHEN pem.verified = true THEN 1 END) as verified_mapping_count, + STRING_AGG(DISTINCT pem.provider, ', ') as providers + FROM exchanges e + LEFT JOIN provider_exchange_mappings pem ON e.id = pem.master_exchange_id + GROUP BY e.id, e.code, e.name, e.country, e.currency, e.active, e.created_at, e.updated_at + ORDER BY e.code + `; + + const result = await postgresClient.query(query); + + return c.json({ + success: true, + data: result.rows, + total: result.rows.length, + }); + } catch (error) { + logger.error('Failed to get exchanges', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Get exchange by ID with detailed provider mappings +exchangeRoutes.get('/:id', async c => { + try { + const exchangeId = c.req.param('id'); + const postgresClient = getPostgreSQLClient(); + + // Get exchange details + const exchangeQuery = 'SELECT * FROM exchanges WHERE id = $1'; + const exchangeResult = await postgresClient.query(exchangeQuery, [exchangeId]); + + if (exchangeResult.rows.length === 0) { + return c.json({ success: false, error: 'Exchange not found' }, 404); + } + + // Get provider mappings for this exchange + const mappingsQuery = ` + SELECT + pem.*, + e.code as master_exchange_code, + e.name as master_exchange_name + FROM provider_exchange_mappings pem + JOIN exchanges e ON pem.master_exchange_id = e.id + WHERE pem.master_exchange_id = $1 + ORDER BY pem.provider, pem.provider_exchange_code + `; + const mappingsResult = await postgresClient.query(mappingsQuery, [exchangeId]); + + return c.json({ + success: true, + data: { + exchange: exchangeResult.rows[0], + provider_mappings: mappingsResult.rows, + }, + }); + } catch (error) { + logger.error('Failed to get exchange details', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Update exchange (activate/deactivate, rename, etc.) +exchangeRoutes.patch('/:id', async c => { + try { + const exchangeId = c.req.param('id'); + const body = await c.req.json(); + const postgresClient = getPostgreSQLClient(); + + const updateFields = []; + const values = []; + let paramIndex = 1; + + // Build dynamic update query + if (body.active !== undefined) { + updateFields.push(`active = $${paramIndex++}`); + values.push(body.active); + } + + if (body.name !== undefined) { + updateFields.push(`name = $${paramIndex++}`); + values.push(body.name); + } + + if (body.country !== undefined) { + updateFields.push(`country = $${paramIndex++}`); + values.push(body.country); + } + + if (body.currency !== undefined) { + updateFields.push(`currency = $${paramIndex++}`); + values.push(body.currency); + } + + if (updateFields.length === 0) { + return c.json({ success: false, error: 'No valid fields to update' }, 400); + } + + updateFields.push(`updated_at = NOW()`); + values.push(exchangeId); + + const query = ` + UPDATE exchanges + SET ${updateFields.join(', ')} + WHERE id = $${paramIndex} + RETURNING * + `; + + const result = await postgresClient.query(query, values); + + if (result.rows.length === 0) { + return c.json({ success: false, error: 'Exchange not found' }, 404); + } + + logger.info('Exchange updated', { exchangeId, updates: body }); + + return c.json({ + success: true, + data: result.rows[0], + message: 'Exchange updated successfully', + }); + } catch (error) { + logger.error('Failed to update exchange', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Get all provider mappings +exchangeRoutes.get('/provider-mappings/all', async c => { + try { + const postgresClient = getPostgreSQLClient(); + + const query = ` + SELECT + pem.*, + e.code as master_exchange_code, + e.name as master_exchange_name, + e.active as master_exchange_active + FROM provider_exchange_mappings pem + JOIN exchanges e ON pem.master_exchange_id = e.id + ORDER BY pem.provider, pem.provider_exchange_code + `; + + const result = await postgresClient.query(query); + + return c.json({ + success: true, + data: result.rows, + total: result.rows.length, + }); + } catch (error) { + logger.error('Failed to get provider mappings', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Get provider mappings by provider +exchangeRoutes.get('/provider-mappings/:provider', async c => { + try { + const provider = c.req.param('provider'); + const postgresClient = getPostgreSQLClient(); + + const query = ` + SELECT + pem.*, + e.code as master_exchange_code, + e.name as master_exchange_name, + e.active as master_exchange_active + FROM provider_exchange_mappings pem + JOIN exchanges e ON pem.master_exchange_id = e.id + WHERE pem.provider = $1 + ORDER BY pem.provider_exchange_code + `; + + const result = await postgresClient.query(query, [provider]); + + return c.json({ + success: true, + data: result.rows, + total: result.rows.length, + provider, + }); + } catch (error) { + logger.error('Failed to get provider mappings', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Update provider mapping (activate/deactivate, verify, change confidence) +exchangeRoutes.patch('/provider-mappings/:id', async c => { + try { + const mappingId = c.req.param('id'); + const body = await c.req.json(); + const postgresClient = getPostgreSQLClient(); + + const updateFields = []; + const values = []; + let paramIndex = 1; + + // Build dynamic update query + if (body.active !== undefined) { + updateFields.push(`active = $${paramIndex++}`); + values.push(body.active); + } + + if (body.verified !== undefined) { + updateFields.push(`verified = $${paramIndex++}`); + values.push(body.verified); + } + + if (body.confidence !== undefined) { + updateFields.push(`confidence = $${paramIndex++}`); + values.push(body.confidence); + } + + if (body.master_exchange_id !== undefined) { + updateFields.push(`master_exchange_id = $${paramIndex++}`); + values.push(body.master_exchange_id); + } + + if (updateFields.length === 0) { + return c.json({ success: false, error: 'No valid fields to update' }, 400); + } + + updateFields.push(`updated_at = NOW()`); + updateFields.push(`auto_mapped = false`); // Mark as manually managed + values.push(mappingId); + + const query = ` + UPDATE provider_exchange_mappings + SET ${updateFields.join(', ')} + WHERE id = $${paramIndex} + RETURNING * + `; + + const result = await postgresClient.query(query, values); + + if (result.rows.length === 0) { + return c.json({ success: false, error: 'Provider mapping not found' }, 404); + } + + logger.info('Provider mapping updated', { mappingId, updates: body }); + + return c.json({ + success: true, + data: result.rows[0], + message: 'Provider mapping updated successfully', + }); + } catch (error) { + logger.error('Failed to update provider mapping', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Create new provider mapping +exchangeRoutes.post('/provider-mappings', async c => { + try { + const body = await c.req.json(); + const postgresClient = getPostgreSQLClient(); + + const { + provider, + provider_exchange_code, + provider_exchange_name, + master_exchange_id, + country_code, + currency, + confidence = 1.0, + active = false, + verified = false, + } = body; + + if (!provider || !provider_exchange_code || !master_exchange_id) { + return c.json( + { + success: false, + error: 'Missing required fields: provider, provider_exchange_code, master_exchange_id', + }, + 400 + ); + } + + const query = ` + INSERT INTO provider_exchange_mappings + (provider, provider_exchange_code, provider_exchange_name, master_exchange_id, + country_code, currency, confidence, active, verified, auto_mapped) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, false) + RETURNING * + `; + + const result = await postgresClient.query(query, [ + provider, + provider_exchange_code, + provider_exchange_name, + master_exchange_id, + country_code, + currency, + confidence, + active, + verified, + ]); + + logger.info('Provider mapping created', { + provider, + provider_exchange_code, + master_exchange_id, + }); + + return c.json( + { + success: true, + data: result.rows[0], + message: 'Provider mapping created successfully', + }, + 201 + ); + } catch (error) { + logger.error('Failed to create provider mapping', { error }); + + // Handle unique constraint violations + if (error instanceof Error && error.message.includes('duplicate key')) { + return c.json( + { + success: false, + error: 'Provider mapping already exists for this provider and exchange code', + }, + 409 + ); + } + + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Get all available providers +exchangeRoutes.get('/providers/list', async c => { + try { + const postgresClient = getPostgreSQLClient(); + + const query = ` + SELECT DISTINCT provider + FROM provider_exchange_mappings + ORDER BY provider + `; + + const result = await postgresClient.query(query); + + return c.json({ + success: true, + data: result.rows.map(row => row.provider), + }); + } catch (error) { + logger.error('Failed to get providers list', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Get all provider exchanges from MongoDB with mapping status +exchangeRoutes.get('/provider-exchanges/all', async c => { + try { + const postgresClient = getPostgreSQLClient(); + + const mongoClient = getMongoDBClient(); + const db = mongoClient.getDatabase(); + + // Get all provider exchanges from different MongoDB collections + const [eodExchanges, ibExchanges, qmExchanges, unifiedExchanges] = await Promise.all([ + db.collection('eodExchanges').find({ active: true }).toArray(), + db.collection('ibExchanges').find({}).toArray(), + db.collection('qmExchanges').find({}).toArray(), + db.collection('exchanges').find({}).toArray(), + ]); + + // Get existing mappings to mark which are already mapped + const existingMappingsQuery = ` + SELECT provider, provider_exchange_code, master_exchange_id, e.code as master_exchange_code + FROM provider_exchange_mappings pem + JOIN exchanges e ON pem.master_exchange_id = e.id + `; + const existingMappings = await postgresClient.query(existingMappingsQuery); + const mappingLookup = new Map(); + existingMappings.rows.forEach(row => { + mappingLookup.set(`${row.provider}:${row.provider_exchange_code}`, { + mapped: true, + master_exchange_id: row.master_exchange_id, + master_exchange_code: row.master_exchange_code, + }); + }); + + // Combine all provider exchanges + const allProviderExchanges = []; + + // EOD exchanges + eodExchanges.forEach(exchange => { + const key = `eod:${exchange.Code}`; + const mapping = mappingLookup.get(key); + allProviderExchanges.push({ + provider: 'eod', + provider_exchange_code: exchange.Code, + provider_exchange_name: exchange.Name, + country_code: exchange.CountryISO2, + currency: exchange.Currency, + symbol_count: null, + is_mapped: !!mapping, + mapped_to_exchange_id: mapping?.master_exchange_id || null, + mapped_to_exchange_code: mapping?.master_exchange_code || null, + }); + }); + + // IB exchanges + ibExchanges.forEach(exchange => { + const key = `ib:${exchange.exchange_id}`; + const mapping = mappingLookup.get(key); + allProviderExchanges.push({ + provider: 'ib', + provider_exchange_code: exchange.exchange_id, + provider_exchange_name: exchange.name, + country_code: exchange.country_code, + currency: null, + symbol_count: null, + is_mapped: !!mapping, + mapped_to_exchange_id: mapping?.master_exchange_id || null, + mapped_to_exchange_code: mapping?.master_exchange_code || null, + }); + }); + + // QM exchanges + qmExchanges.forEach(exchange => { + const key = `qm:${exchange.exchangeCode}`; + const mapping = mappingLookup.get(key); + allProviderExchanges.push({ + provider: 'qm', + provider_exchange_code: exchange.exchangeCode, + provider_exchange_name: exchange.name, + country_code: exchange.countryCode, + currency: exchange.countryCode === 'CA' ? 'CAD' : 'USD', + symbol_count: null, + is_mapped: !!mapping, + mapped_to_exchange_id: mapping?.master_exchange_id || null, + mapped_to_exchange_code: mapping?.master_exchange_code || null, + }); + }); + + // Unified exchanges + unifiedExchanges.forEach(exchange => { + const key = `unified:${exchange.sourceCode || exchange.code}`; + const mapping = mappingLookup.get(key); + allProviderExchanges.push({ + provider: 'unified', + provider_exchange_code: exchange.sourceCode || exchange.code, + provider_exchange_name: exchange.sourceName || exchange.name, + country_code: null, + currency: null, + symbol_count: null, + is_mapped: !!mapping, + mapped_to_exchange_id: mapping?.master_exchange_id || null, + mapped_to_exchange_code: mapping?.master_exchange_code || null, + }); + }); + + return c.json({ + success: true, + data: allProviderExchanges, + total: allProviderExchanges.length, + }); + } catch (error) { + logger.error('Failed to get provider exchanges', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Get unmapped provider exchanges by provider +exchangeRoutes.get('/provider-exchanges/unmapped/:provider', async c => { + try { + const provider = c.req.param('provider'); + const postgresClient = getPostgreSQLClient(); + + const mongoClient = getMongoDBClient(); + const db = mongoClient.getDatabase(); + + // Get existing mappings for this provider + const existingMappingsQuery = ` + SELECT provider_exchange_code + FROM provider_exchange_mappings + WHERE provider = $1 + `; + const existingMappings = await postgresClient.query(existingMappingsQuery, [provider]); + const mappedCodes = new Set(existingMappings.rows.map(row => row.provider_exchange_code)); + + let providerExchanges = []; + + switch (provider) { + case 'eod': + const eodExchanges = await db.collection('eodExchanges').find({ active: true }).toArray(); + providerExchanges = eodExchanges + .filter(exchange => !mappedCodes.has(exchange.Code)) + .map(exchange => ({ + provider_exchange_code: exchange.Code, + provider_exchange_name: exchange.Name, + country_code: exchange.CountryISO2, + currency: exchange.Currency, + symbol_count: null, + })); + break; + + case 'ib': + const ibExchanges = await db.collection('ibExchanges').find({}).toArray(); + providerExchanges = ibExchanges + .filter(exchange => !mappedCodes.has(exchange.exchange_id)) + .map(exchange => ({ + provider_exchange_code: exchange.exchange_id, + provider_exchange_name: exchange.name, + country_code: exchange.country_code, + currency: null, + symbol_count: null, + })); + break; + + case 'qm': + const qmExchanges = await db.collection('qmExchanges').find({}).toArray(); + + providerExchanges = qmExchanges + .filter(exchange => !mappedCodes.has(exchange.exchangeCode)) + .map(exchange => ({ + provider_exchange_code: exchange.exchangeCode, + provider_exchange_name: exchange.name, + country_code: exchange.countryCode, + currency: exchange.countryCode === 'CA' ? 'CAD' : 'USD', + symbol_count: null, + })); + break; + + case 'unified': + const unifiedExchanges = await db.collection('exchanges').find({}).toArray(); + providerExchanges = unifiedExchanges + .filter(exchange => !mappedCodes.has(exchange.sourceCode || exchange.code)) + .map(exchange => ({ + provider_exchange_code: exchange.sourceCode || exchange.code, + provider_exchange_name: exchange.sourceName || exchange.name, + country_code: null, + currency: null, + symbol_count: null, + })); + break; + + default: + return c.json( + { + success: false, + error: `Unknown provider: ${provider}`, + }, + 400 + ); + } + + return c.json({ + success: true, + data: providerExchanges, + total: providerExchanges.length, + provider, + }); + } catch (error) { + logger.error('Failed to get unmapped provider exchanges', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); + +// Get exchange statistics +exchangeRoutes.get('/stats/summary', async c => { + try { + const postgresClient = getPostgreSQLClient(); + + const query = ` + SELECT + (SELECT COUNT(*) FROM exchanges) as total_exchanges, + (SELECT COUNT(*) FROM exchanges WHERE active = true) as active_exchanges, + (SELECT COUNT(DISTINCT country) FROM exchanges) as countries, + (SELECT COUNT(DISTINCT currency) FROM exchanges) as currencies, + (SELECT COUNT(*) FROM provider_exchange_mappings) as total_provider_mappings, + (SELECT COUNT(*) FROM provider_exchange_mappings WHERE active = true) as active_provider_mappings, + (SELECT COUNT(*) FROM provider_exchange_mappings WHERE verified = true) as verified_provider_mappings, + (SELECT COUNT(DISTINCT provider) FROM provider_exchange_mappings) as providers + `; + + const result = await postgresClient.query(query); + + return c.json({ + success: true, + data: result.rows[0], + }); + } catch (error) { + logger.error('Failed to get exchange statistics', { error }); + return c.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + 500 + ); + } +}); diff --git a/apps/web-api/src/routes/health.routes.ts b/apps/web-api/src/routes/health.routes.ts new file mode 100644 index 0000000..217fd92 --- /dev/null +++ b/apps/web-api/src/routes/health.routes.ts @@ -0,0 +1,69 @@ +/** + * Health check routes + */ +import { Hono } from 'hono'; +import { getLogger } from '@stock-bot/logger'; +import { getMongoDBClient } from '@stock-bot/mongodb-client'; +import { getPostgreSQLClient } from '@stock-bot/postgres-client'; + +const logger = getLogger('health-routes'); +export const healthRoutes = new Hono(); + +// Basic health check +healthRoutes.get('/', c => { + return c.json({ + status: 'healthy', + service: 'web-api', + timestamp: new Date().toISOString(), + }); +}); + +// Detailed health check with database connectivity +healthRoutes.get('/detailed', async c => { + const health = { + status: 'healthy', + service: 'web-api', + timestamp: new Date().toISOString(), + checks: { + mongodb: { status: 'unknown', message: '' }, + postgresql: { status: 'unknown', message: '' }, + }, + }; + + // Check MongoDB + try { + const mongoClient = getMongoDBClient(); + if (mongoClient.connected) { + // Try a simple operation + const db = mongoClient.getDatabase(); + await db.admin().ping(); + health.checks.mongodb = { status: 'healthy', message: 'Connected and responsive' }; + } else { + health.checks.mongodb = { status: 'unhealthy', message: 'Not connected' }; + } + } catch (error) { + health.checks.mongodb = { + status: 'unhealthy', + message: error instanceof Error ? error.message : 'Unknown error', + }; + } + + // Check PostgreSQL + try { + const postgresClient = getPostgreSQLClient(); + await postgresClient.query('SELECT 1'); + health.checks.postgresql = { status: 'healthy', message: 'Connected and responsive' }; + } catch (error) { + health.checks.postgresql = { + status: 'unhealthy', + message: error instanceof Error ? error.message : 'Unknown error', + }; + } + + // Overall status + const allHealthy = Object.values(health.checks).every(check => check.status === 'healthy'); + health.status = allHealthy ? 'healthy' : 'unhealthy'; + + const statusCode = allHealthy ? 200 : 503; + return c.json(health, statusCode); +}); diff --git a/apps/web-api/src/routes/index.ts b/apps/web-api/src/routes/index.ts new file mode 100644 index 0000000..8a1e802 --- /dev/null +++ b/apps/web-api/src/routes/index.ts @@ -0,0 +1,5 @@ +/** + * Routes index - exports all route modules + */ +export { exchangeRoutes } from './exchange.routes'; +export { healthRoutes } from './health.routes'; diff --git a/apps/web-api/tsconfig.json b/apps/web-api/tsconfig.json new file mode 100644 index 0000000..ed1857d --- /dev/null +++ b/apps/web-api/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} diff --git a/apps/web-api/turbo.json b/apps/web-api/turbo.json new file mode 100644 index 0000000..d6b68e4 --- /dev/null +++ b/apps/web-api/turbo.json @@ -0,0 +1,13 @@ +{ + "extends": ["//"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "dev": { + "cache": false, + "persistent": true + } + } +} diff --git a/apps/web/.env.example b/apps/web-app/.env.example similarity index 100% rename from apps/web/.env.example rename to apps/web-app/.env.example diff --git a/apps/web/README.md b/apps/web-app/README.md similarity index 100% rename from apps/web/README.md rename to apps/web-app/README.md diff --git a/apps/web/eslint.config.js b/apps/web-app/eslint.config.js similarity index 100% rename from apps/web/eslint.config.js rename to apps/web-app/eslint.config.js diff --git a/apps/web/index.html b/apps/web-app/index.html similarity index 100% rename from apps/web/index.html rename to apps/web-app/index.html diff --git a/apps/web/package.json b/apps/web-app/package.json similarity index 97% rename from apps/web/package.json rename to apps/web-app/package.json index 83b3112..f3001b4 100644 --- a/apps/web/package.json +++ b/apps/web-app/package.json @@ -1,5 +1,5 @@ { - "name": "@stock-bot/web", + "name": "@stock-bot/web-app", "version": "0.1.0", "private": true, "scripts": { diff --git a/apps/web/postcss.config.js b/apps/web-app/postcss.config.js similarity index 100% rename from apps/web/postcss.config.js rename to apps/web-app/postcss.config.js diff --git a/apps/web/public/vite.svg b/apps/web-app/public/vite.svg similarity index 100% rename from apps/web/public/vite.svg rename to apps/web-app/public/vite.svg diff --git a/apps/web/src/App.tsx b/apps/web-app/src/App.tsx similarity index 100% rename from apps/web/src/App.tsx rename to apps/web-app/src/App.tsx diff --git a/apps/web/src/app/App.tsx b/apps/web-app/src/app/App.tsx similarity index 100% rename from apps/web/src/app/App.tsx rename to apps/web-app/src/app/App.tsx diff --git a/apps/web/src/app/index.ts b/apps/web-app/src/app/index.ts similarity index 100% rename from apps/web/src/app/index.ts rename to apps/web-app/src/app/index.ts diff --git a/apps/web/src/components/index.ts b/apps/web-app/src/components/index.ts similarity index 100% rename from apps/web/src/components/index.ts rename to apps/web-app/src/components/index.ts diff --git a/apps/web/src/components/layout/Header.tsx b/apps/web-app/src/components/layout/Header.tsx similarity index 100% rename from apps/web/src/components/layout/Header.tsx rename to apps/web-app/src/components/layout/Header.tsx diff --git a/apps/web/src/components/layout/Layout.tsx b/apps/web-app/src/components/layout/Layout.tsx similarity index 100% rename from apps/web/src/components/layout/Layout.tsx rename to apps/web-app/src/components/layout/Layout.tsx diff --git a/apps/web/src/components/layout/Sidebar.tsx b/apps/web-app/src/components/layout/Sidebar.tsx similarity index 100% rename from apps/web/src/components/layout/Sidebar.tsx rename to apps/web-app/src/components/layout/Sidebar.tsx diff --git a/apps/web/src/components/layout/index.ts b/apps/web-app/src/components/layout/index.ts similarity index 100% rename from apps/web/src/components/layout/index.ts rename to apps/web-app/src/components/layout/index.ts diff --git a/apps/web/src/components/ui/Card.tsx b/apps/web-app/src/components/ui/Card.tsx similarity index 100% rename from apps/web/src/components/ui/Card.tsx rename to apps/web-app/src/components/ui/Card.tsx diff --git a/apps/web/src/components/ui/DataTable/DataTable.tsx b/apps/web-app/src/components/ui/DataTable/DataTable.tsx similarity index 100% rename from apps/web/src/components/ui/DataTable/DataTable.tsx rename to apps/web-app/src/components/ui/DataTable/DataTable.tsx diff --git a/apps/web/src/components/ui/DataTable/index.ts b/apps/web-app/src/components/ui/DataTable/index.ts similarity index 100% rename from apps/web/src/components/ui/DataTable/index.ts rename to apps/web-app/src/components/ui/DataTable/index.ts diff --git a/apps/web/src/components/ui/DataTable/types.ts b/apps/web-app/src/components/ui/DataTable/types.ts similarity index 100% rename from apps/web/src/components/ui/DataTable/types.ts rename to apps/web-app/src/components/ui/DataTable/types.ts diff --git a/apps/web/src/components/ui/StatCard.tsx b/apps/web-app/src/components/ui/StatCard.tsx similarity index 100% rename from apps/web/src/components/ui/StatCard.tsx rename to apps/web-app/src/components/ui/StatCard.tsx diff --git a/apps/web-app/src/components/ui/button.tsx b/apps/web-app/src/components/ui/button.tsx new file mode 100644 index 0000000..2f8c806 --- /dev/null +++ b/apps/web-app/src/components/ui/button.tsx @@ -0,0 +1,38 @@ +import React from 'react'; + +interface ButtonProps extends React.ButtonHTMLAttributes { + variant?: 'default' | 'outline' | 'danger'; + size?: 'sm' | 'md' | 'lg'; + children: React.ReactNode; +} + +export function Button({ + variant = 'default', + size = 'md', + className = '', + children, + disabled, + ...props +}: ButtonProps) { + const baseClasses = 'inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed'; + + const variantClasses = { + default: 'bg-primary-500 text-white hover:bg-primary-600 focus:ring-primary-500', + outline: 'border border-border bg-transparent text-text-primary hover:bg-surface-secondary focus:ring-primary-500', + danger: 'bg-red-500 text-white hover:bg-red-600 focus:ring-red-500', + }; + + const sizeClasses = { + sm: 'px-2 py-1 text-xs', + md: 'px-4 py-2 text-sm', + lg: 'px-6 py-3 text-base', + }; + + const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`; + + return ( + + ); +} \ No newline at end of file diff --git a/apps/web-app/src/components/ui/dialog.tsx b/apps/web-app/src/components/ui/dialog.tsx new file mode 100644 index 0000000..ea342a2 --- /dev/null +++ b/apps/web-app/src/components/ui/dialog.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { XMarkIcon } from '@heroicons/react/24/outline'; + +interface DialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + children: React.ReactNode; +} + +export function Dialog({ open, onOpenChange, children }: DialogProps) { + if (!open) return null; + + return ( +
+ {/* Backdrop */} +
onOpenChange(false)} + /> + {/* Dialog */} +
+ {children} +
+
+ ); +} + +interface DialogContentProps { + children: React.ReactNode; + className?: string; +} + +export function DialogContent({ children, className = '' }: DialogContentProps) { + return ( +
+ {children} +
+ ); +} + +interface DialogHeaderProps { + children: React.ReactNode; + className?: string; +} + +export function DialogHeader({ children, className = '' }: DialogHeaderProps) { + return
{children}
; +} + +interface DialogTitleProps { + children: React.ReactNode; + className?: string; +} + +export function DialogTitle({ children, className = '' }: DialogTitleProps) { + return

{children}

; +} \ No newline at end of file diff --git a/apps/web/src/components/ui/index.ts b/apps/web-app/src/components/ui/index.ts similarity index 54% rename from apps/web/src/components/ui/index.ts rename to apps/web-app/src/components/ui/index.ts index 56d1510..30766a7 100644 --- a/apps/web/src/components/ui/index.ts +++ b/apps/web-app/src/components/ui/index.ts @@ -1,3 +1,5 @@ export { Card, CardHeader, CardContent } from './Card'; export { StatCard } from './StatCard'; export { DataTable } from './DataTable'; +export { Dialog, DialogContent, DialogHeader, DialogTitle } from './dialog'; +export { Button } from './button'; diff --git a/apps/web/src/features/dashboard/DashboardPage.tsx b/apps/web-app/src/features/dashboard/DashboardPage.tsx similarity index 100% rename from apps/web/src/features/dashboard/DashboardPage.tsx rename to apps/web-app/src/features/dashboard/DashboardPage.tsx diff --git a/apps/web/src/features/dashboard/components/DashboardActivity.tsx b/apps/web-app/src/features/dashboard/components/DashboardActivity.tsx similarity index 100% rename from apps/web/src/features/dashboard/components/DashboardActivity.tsx rename to apps/web-app/src/features/dashboard/components/DashboardActivity.tsx diff --git a/apps/web/src/features/dashboard/components/DashboardStats.tsx b/apps/web-app/src/features/dashboard/components/DashboardStats.tsx similarity index 100% rename from apps/web/src/features/dashboard/components/DashboardStats.tsx rename to apps/web-app/src/features/dashboard/components/DashboardStats.tsx diff --git a/apps/web/src/features/dashboard/components/PortfolioTable.tsx b/apps/web-app/src/features/dashboard/components/PortfolioTable.tsx similarity index 100% rename from apps/web/src/features/dashboard/components/PortfolioTable.tsx rename to apps/web-app/src/features/dashboard/components/PortfolioTable.tsx diff --git a/apps/web/src/features/dashboard/components/index.ts b/apps/web-app/src/features/dashboard/components/index.ts similarity index 100% rename from apps/web/src/features/dashboard/components/index.ts rename to apps/web-app/src/features/dashboard/components/index.ts diff --git a/apps/web/src/features/dashboard/index.ts b/apps/web-app/src/features/dashboard/index.ts similarity index 100% rename from apps/web/src/features/dashboard/index.ts rename to apps/web-app/src/features/dashboard/index.ts diff --git a/apps/web/src/features/exchanges/ExchangesPage.tsx b/apps/web-app/src/features/exchanges/ExchangesPage.tsx similarity index 100% rename from apps/web/src/features/exchanges/ExchangesPage.tsx rename to apps/web-app/src/features/exchanges/ExchangesPage.tsx diff --git a/apps/web-app/src/features/exchanges/components/AddProviderMappingDialog.tsx b/apps/web-app/src/features/exchanges/components/AddProviderMappingDialog.tsx new file mode 100644 index 0000000..4db8abf --- /dev/null +++ b/apps/web-app/src/features/exchanges/components/AddProviderMappingDialog.tsx @@ -0,0 +1,251 @@ +import { Dialog, DialogContent, DialogHeader, DialogTitle, Button } from '@/components/ui'; +import { useCallback, useEffect, useState } from 'react'; +import { useExchanges } from '../hooks/useExchanges'; +import { CreateProviderMappingRequest } from '../types'; + +interface AddProviderMappingDialogProps { + isOpen: boolean; + exchangeId: string; + exchangeName: string; + onClose: () => void; + onCreateMapping: (request: CreateProviderMappingRequest) => Promise; +} + +export function AddProviderMappingDialog({ + isOpen, + exchangeId, + exchangeName, + onClose, + onCreateMapping, +}: AddProviderMappingDialogProps) { + const { fetchProviders, fetchUnmappedProviderExchanges } = useExchanges(); + const [providers, setProviders] = useState([]); + const [selectedProvider, setSelectedProvider] = useState(''); + const [unmappedExchanges, setUnmappedExchanges] = useState([]); + const [selectedProviderExchange, setSelectedProviderExchange] = useState(''); + const [loading, setLoading] = useState(false); + const [providersLoading, setProvidersLoading] = useState(false); + const [exchangesLoading, setExchangesLoading] = useState(false); + + // Load providers on mount + useEffect(() => { + if (isOpen) { + loadProviders(); + } + }, [isOpen]); + + // Load unmapped exchanges when provider changes + useEffect(() => { + if (selectedProvider) { + loadUnmappedExchanges(selectedProvider); + } else { + setUnmappedExchanges([]); + setSelectedProviderExchange(''); + } + }, [selectedProvider]); + + const loadProviders = useCallback(async () => { + setProvidersLoading(true); + try { + const providersData = await fetchProviders(); + setProviders(providersData); + } catch (error) { + console.error('Error loading providers:', error); + } finally { + setProvidersLoading(false); + } + }, [fetchProviders]); + + const loadUnmappedExchanges = useCallback( + async (provider: string) => { + setExchangesLoading(true); + try { + const exchangesData = await fetchUnmappedProviderExchanges(provider); + setUnmappedExchanges(exchangesData); + } catch (error) { + console.error('Error loading unmapped exchanges:', error); + } finally { + setExchangesLoading(false); + } + }, + [fetchUnmappedProviderExchanges] + ); + + const handleSubmit = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + + if (!selectedProvider || !selectedProviderExchange) { + return; + } + + const selectedExchange = unmappedExchanges.find( + exchange => exchange.provider_exchange_code === selectedProviderExchange + ); + + if (!selectedExchange) { + return; + } + + setLoading(true); + try { + const request: CreateProviderMappingRequest = { + provider: selectedProvider, + provider_exchange_code: selectedExchange.provider_exchange_code, + provider_exchange_name: selectedExchange.provider_exchange_name, + master_exchange_id: exchangeId, + country_code: selectedExchange.country_code, + currency: selectedExchange.currency, + confidence: 1.0, + active: false, + verified: false, + }; + + await onCreateMapping(request); + } catch (error) { + console.error('Error creating provider mapping:', error); + } finally { + setLoading(false); + } + }, + [selectedProvider, selectedProviderExchange, unmappedExchanges, exchangeId, onCreateMapping] + ); + + const handleClose = useCallback(() => { + setSelectedProvider(''); + setSelectedProviderExchange(''); + setUnmappedExchanges([]); + onClose(); + }, [onClose]); + + return ( + + + + Add Provider Mapping +

+ Map a provider exchange to {exchangeName} +

+
+ +
+ {/* Provider Selection */} +
+ + +
+ + {/* Provider Exchange Selection */} +
+ + + {selectedProvider && unmappedExchanges.length === 0 && !exchangesLoading && ( +

+ All exchanges for this provider are already mapped. +

+ )} +
+ + {/* Selected Exchange Info */} + {selectedProviderExchange && ( +
+

Selected Exchange Info

+ {(() => { + const exchange = unmappedExchanges.find( + ex => ex.provider_exchange_code === selectedProviderExchange + ); + if (!exchange) return null; + + return ( +
+
+ Code:{' '} + {exchange.provider_exchange_code} +
+
+ Name: {exchange.provider_exchange_name} +
+ {exchange.country_code && ( +
+ Country: {exchange.country_code} +
+ )} + {exchange.currency && ( +
+ Currency: {exchange.currency} +
+ )} + {exchange.symbol_count && ( +
+ Symbols: {exchange.symbol_count} +
+ )} +
+ ); + })()} +
+ )} + + {/* Action Buttons */} +
+ + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/apps/web/src/features/exchanges/components/AddSourceDialog.tsx b/apps/web-app/src/features/exchanges/components/AddSourceDialog.tsx similarity index 100% rename from apps/web/src/features/exchanges/components/AddSourceDialog.tsx rename to apps/web-app/src/features/exchanges/components/AddSourceDialog.tsx diff --git a/apps/web-app/src/features/exchanges/components/ExchangesTable.tsx b/apps/web-app/src/features/exchanges/components/ExchangesTable.tsx new file mode 100644 index 0000000..b0314a4 --- /dev/null +++ b/apps/web-app/src/features/exchanges/components/ExchangesTable.tsx @@ -0,0 +1,424 @@ +import { DataTable } from '@/components/ui'; +import { PlusIcon, XMarkIcon, CheckIcon } from '@heroicons/react/24/outline'; +import { ColumnDef } from '@tanstack/react-table'; +import { useCallback, useMemo, useState, useEffect } from 'react'; +import { useExchanges } from '../hooks/useExchanges'; +import { Exchange, ProviderMapping } from '../types'; +import { AddProviderMappingDialog } from './AddProviderMappingDialog'; + +export function ExchangesTable() { + const { + exchanges, + loading, + error, + updateExchange, + fetchExchangeDetails, + fetchProviderMappings, + updateProviderMapping, + createProviderMapping, + refetch + } = useExchanges(); + const [editingCell, setEditingCell] = useState<{ id: string; field: string } | null>(null); + const [editValue, setEditValue] = useState(''); + const [addProviderDialog, setAddProviderDialog] = useState<{ + exchangeId: string; + exchangeName: string; + } | null>(null); + const [expandedRows, setExpandedRows] = useState>(new Set()); + const [expandedRowData, setExpandedRowData] = useState>({}); + + const handleCellEdit = useCallback( + async (id: string, field: string, value: string) => { + if (field === 'name') { + await updateExchange(id, { name: value }); + } + setEditingCell(null); + setEditValue(''); + }, + [updateExchange] + ); + + const handleToggleActive = useCallback( + async (id: string, currentStatus: boolean) => { + await updateExchange(id, { active: !currentStatus }); + }, + [updateExchange] + ); + + const handleAddProviderMapping = useCallback(async (exchangeId: string, exchangeName: string) => { + setAddProviderDialog({ exchangeId, exchangeName }); + }, []); + + const handleToggleProviderMapping = useCallback( + async (mappingId: string, currentStatus: boolean) => { + const success = await updateProviderMapping(mappingId, { active: !currentStatus }); + if (success) { + refetch(); + } + }, + [updateProviderMapping, refetch] + ); + + const handleToggleExpandRow = useCallback(async (rowId: string) => { + setExpandedRows(prev => { + const next = new Set(prev); + if (next.has(rowId)) { + next.delete(rowId); + } else { + next.add(rowId); + // Load provider mappings for this exchange + if (!expandedRowData[rowId]) { + fetchExchangeDetails(rowId).then(details => { + if (details) { + setExpandedRowData(prev => ({ + ...prev, + [rowId]: details.provider_mappings + })); + } + }); + } + } + return next; + }); + }, [fetchExchangeDetails, expandedRowData]); + + const columns = useMemo[]>(() => { + return [ + { + id: 'expand', + header: '', + size: 30, + enableResizing: false, + cell: ({ row }) => { + const isExpanded = expandedRows.has(row.original.id); + return ( + + ); + }, + }, + { + id: 'id', + header: 'ID', + accessorKey: 'id', + size: 50, + enableResizing: false, + cell: ({ getValue, cell }) => ( + + {getValue() as string} + + ), + }, + { + id: 'code', + header: 'Code', + accessorKey: 'code', + size: 80, + enableResizing: false, + cell: ({ getValue, cell }) => ( + + {getValue() as string} + + ), + }, + { + id: 'name', + header: 'Name', + accessorKey: 'name', + size: 200, + maxSize: 300, + enableResizing: true, + cell: ({ getValue, row, cell }) => { + const isEditing = + editingCell?.id === row.original.id && editingCell?.field === 'name'; + + if (isEditing) { + return ( + setEditValue(e.target.value)} + onBlur={() => handleCellEdit(row.original.id, 'name', editValue)} + onKeyDown={e => { + if (e.key === 'Enter') { + handleCellEdit(row.original.id, 'name', editValue); + } else if (e.key === 'Escape') { + setEditingCell(null); + setEditValue(''); + } + }} + className="w-full bg-surface border border-border rounded px-2 py-1 text-sm" + autoFocus + /> + ); + } + + return ( +
{ + setEditingCell({ id: row.original.id, field: 'name' }); + setEditValue(getValue() as string); + }} + > + {getValue() as string} +
+ ); + }, + }, + { + id: 'country', + header: 'Country', + accessorKey: 'country', + size: 80, + maxSize: 80, + cell: ({ getValue }) => ( + {getValue() as string} + ), + }, + { + id: 'currency', + header: 'Currency', + accessorKey: 'currency', + size: 70, + cell: ({ getValue, cell }) => ( + + {getValue() as string} + + ), + }, + { + id: 'active', + header: 'Active', + accessorKey: 'active', + size: 80, + maxSize: 80, + cell: ({ getValue, row, cell }) => { + const isActive = getValue() as boolean; + return ( + + ); + }, + }, + { + id: 'provider_mappings', + header: 'Provider Mappings', + accessorKey: 'provider_mapping_count', + size: 150, + cell: ({ getValue, row }) => { + const totalMappings = parseInt(getValue() as string) || 0; + const activeMappings = parseInt(row.original.active_mapping_count) || 0; + const verifiedMappings = parseInt(row.original.verified_mapping_count) || 0; + const providers = row.original.providers; + + return ( +
+
+ {totalMappings} + total +
+
+ + + {activeMappings} active + + + ✓ {verifiedMappings} verified + +
+ {providers && ( +
+ {providers} +
+ )} +
+ ); + }, + }, + { + id: 'actions', + header: 'Actions', + size: 100, + cell: ({ row }) => ( + + ), + }, + { + id: 'updated_at', + header: 'Last Updated', + accessorKey: 'updated_at', + size: 120, + maxSize: 120, + cell: ({ getValue }) => ( + + {new Date(getValue() as string).toLocaleDateString()} + + ), + }, + ]; + }, [ + editingCell, + editValue, + expandedRows, + handleCellEdit, + handleToggleActive, + handleAddProviderMapping, + handleToggleExpandRow, + ]); + + if (error) { + return ( +
+

Error Loading Exchanges

+

{error}

+

+ Make sure the web-api service is running on localhost:4000 +

+
+ ); + } + + const renderExpandedRow = (exchange: Exchange) => { + const mappings = expandedRowData[exchange.id] || []; + + if (mappings.length === 0) { + return ( +
+
No provider mappings found for this exchange.
+ +
+ ); + } + + return ( +
+

Provider Mappings

+
+ {mappings.map((mapping) => ( +
+
+
+ + {mapping.provider.toUpperCase()} + + + {mapping.provider_exchange_code} + + + {mapping.provider_exchange_name} + + {mapping.country_code && ( + + {mapping.country_code} + + )} +
+
+ Confidence: {mapping.confidence} + Created: {new Date(mapping.created_at).toLocaleDateString()} + {mapping.auto_mapped && Auto-mapped} +
+
+
+
+ {mapping.verified && ( + + ✓ + + )} + +
+
+
+ ))} +
+
+ ); + }; + + return ( + <> +
+ + + {/* Expanded rows */} + {Array.from(expandedRows).map(exchangeId => { + const exchange = exchanges?.find(e => e.id === exchangeId); + if (!exchange) return null; + + return ( +
+ {renderExpandedRow(exchange)} +
+ ); + })} +
+ + {addProviderDialog && ( + setAddProviderDialog(null)} + onCreateMapping={async (mappingRequest) => { + const result = await createProviderMapping(mappingRequest); + if (result) { + setAddProviderDialog(null); + refetch(); + } + }} + /> + )} + + ); +} diff --git a/apps/web/src/features/exchanges/components/index.ts b/apps/web-app/src/features/exchanges/components/index.ts similarity index 59% rename from apps/web/src/features/exchanges/components/index.ts rename to apps/web-app/src/features/exchanges/components/index.ts index f056547..61ef777 100644 --- a/apps/web/src/features/exchanges/components/index.ts +++ b/apps/web-app/src/features/exchanges/components/index.ts @@ -1,2 +1,3 @@ export { AddSourceDialog } from './AddSourceDialog'; +export { AddProviderMappingDialog } from './AddProviderMappingDialog'; export { ExchangesTable } from './ExchangesTable'; diff --git a/apps/web/src/features/exchanges/hooks/index.ts b/apps/web-app/src/features/exchanges/hooks/index.ts similarity index 100% rename from apps/web/src/features/exchanges/hooks/index.ts rename to apps/web-app/src/features/exchanges/hooks/index.ts diff --git a/apps/web-app/src/features/exchanges/hooks/useExchanges.ts b/apps/web-app/src/features/exchanges/hooks/useExchanges.ts new file mode 100644 index 0000000..51663e0 --- /dev/null +++ b/apps/web-app/src/features/exchanges/hooks/useExchanges.ts @@ -0,0 +1,269 @@ +import { useCallback, useEffect, useState } from 'react'; +import { + CreateProviderMappingRequest, + Exchange, + ExchangeDetails, + ExchangeStats, + ProviderMapping, + ProviderExchange, + UpdateExchangeRequest, + UpdateProviderMappingRequest, +} from '../types'; + +const API_BASE_URL = 'http://localhost:4000/api'; + +export function useExchanges() { + const [exchanges, setExchanges] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const fetchExchanges = useCallback(async () => { + try { + setLoading(true); + setError(null); + + const response = await fetch(`${API_BASE_URL}/exchanges`); + + if (!response.ok) { + throw new Error(`Failed to fetch exchanges: ${response.statusText}`); + } + + const data = await response.json(); + + if (data.success) { + setExchanges(data.data || []); + } else { + throw new Error(data.error || 'API returned error status'); + } + } catch (err) { + console.error('Error fetching exchanges:', err); + setError(err instanceof Error ? err.message : 'Failed to fetch exchanges'); + setExchanges([]); + } finally { + setLoading(false); + } + }, []); + + const updateExchange = useCallback( + async (id: string, updates: UpdateExchangeRequest) => { + try { + const response = await fetch(`${API_BASE_URL}/exchanges/${id}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(updates), + }); + + if (!response.ok) { + throw new Error(`Failed to update exchange: ${response.statusText}`); + } + + const result = await response.json(); + if (!result.success) { + throw new Error(result.error || 'Failed to update exchange'); + } + + // Refresh the exchanges list + await fetchExchanges(); + return true; + } catch (err) { + console.error('Error updating exchange:', err); + setError(err instanceof Error ? err.message : 'Failed to update exchange'); + return false; + } + }, + [fetchExchanges] + ); + + const fetchExchangeDetails = useCallback(async (id: string): Promise => { + try { + const response = await fetch(`${API_BASE_URL}/exchanges/${id}`); + + if (!response.ok) { + throw new Error(`Failed to fetch exchange details: ${response.statusText}`); + } + + const result = await response.json(); + if (!result.success) { + throw new Error(result.error || 'Failed to fetch exchange details'); + } + + return result.data; + } catch (err) { + console.error('Error fetching exchange details:', err); + setError(err instanceof Error ? err.message : 'Failed to fetch exchange details'); + return null; + } + }, []); + + const fetchStats = useCallback(async (): Promise => { + try { + const response = await fetch(`${API_BASE_URL}/exchanges/stats/summary`); + + if (!response.ok) { + throw new Error(`Failed to fetch stats: ${response.statusText}`); + } + + const result = await response.json(); + if (!result.success) { + throw new Error(result.error || 'Failed to fetch stats'); + } + + return result.data; + } catch (err) { + console.error('Error fetching stats:', err); + setError(err instanceof Error ? err.message : 'Failed to fetch stats'); + return null; + } + }, []); + + const fetchProviderMappings = useCallback( + async (provider?: string): Promise => { + try { + const url = provider + ? `${API_BASE_URL}/exchanges/provider-mappings/${provider}` + : `${API_BASE_URL}/exchanges/provider-mappings/all`; + + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`Failed to fetch provider mappings: ${response.statusText}`); + } + + const result = await response.json(); + if (!result.success) { + throw new Error(result.error || 'Failed to fetch provider mappings'); + } + + return result.data || []; + } catch (err) { + console.error('Error fetching provider mappings:', err); + setError(err instanceof Error ? err.message : 'Failed to fetch provider mappings'); + return []; + } + }, + [] + ); + + const updateProviderMapping = useCallback( + async (id: string, updates: UpdateProviderMappingRequest) => { + try { + const response = await fetch(`${API_BASE_URL}/exchanges/provider-mappings/${id}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(updates), + }); + + if (!response.ok) { + throw new Error(`Failed to update provider mapping: ${response.statusText}`); + } + + const result = await response.json(); + if (!result.success) { + throw new Error(result.error || 'Failed to update provider mapping'); + } + + return true; + } catch (err) { + console.error('Error updating provider mapping:', err); + setError(err instanceof Error ? err.message : 'Failed to update provider mapping'); + return false; + } + }, + [] + ); + + const createProviderMapping = useCallback(async (request: CreateProviderMappingRequest) => { + try { + const response = await fetch(`${API_BASE_URL}/exchanges/provider-mappings`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(request), + }); + + if (!response.ok) { + throw new Error(`Failed to create provider mapping: ${response.statusText}`); + } + + const result = await response.json(); + if (!result.success) { + throw new Error(result.error || 'Failed to create provider mapping'); + } + + return result.data; + } catch (err) { + console.error('Error creating provider mapping:', err); + setError(err instanceof Error ? err.message : 'Failed to create provider mapping'); + return null; + } + }, []); + + const fetchProviders = useCallback(async (): Promise => { + try { + const response = await fetch(`${API_BASE_URL}/exchanges/providers/list`); + + if (!response.ok) { + throw new Error(`Failed to fetch providers: ${response.statusText}`); + } + + const result = await response.json(); + if (!result.success) { + throw new Error(result.error || 'Failed to fetch providers'); + } + + return result.data || []; + } catch (err) { + console.error('Error fetching providers:', err); + setError(err instanceof Error ? err.message : 'Failed to fetch providers'); + return []; + } + }, []); + + const fetchUnmappedProviderExchanges = useCallback( + async (provider: string): Promise => { + try { + const response = await fetch(`${API_BASE_URL}/exchanges/provider-exchanges/unmapped/${provider}`); + + if (!response.ok) { + throw new Error(`Failed to fetch unmapped exchanges: ${response.statusText}`); + } + + const result = await response.json(); + if (!result.success) { + throw new Error(result.error || 'Failed to fetch unmapped exchanges'); + } + + return result.data || []; + } catch (err) { + console.error('Error fetching unmapped exchanges:', err); + setError(err instanceof Error ? err.message : 'Failed to fetch unmapped exchanges'); + return []; + } + }, + [] + ); + + useEffect(() => { + fetchExchanges(); + }, [fetchExchanges]); + + return { + exchanges, + loading, + error, + refetch: fetchExchanges, + updateExchange, + fetchExchangeDetails, + fetchStats, + fetchProviderMappings, + updateProviderMapping, + createProviderMapping, + fetchProviders, + fetchUnmappedProviderExchanges, + }; +} diff --git a/apps/web/src/features/exchanges/index.ts b/apps/web-app/src/features/exchanges/index.ts similarity index 100% rename from apps/web/src/features/exchanges/index.ts rename to apps/web-app/src/features/exchanges/index.ts diff --git a/apps/web-app/src/features/exchanges/types/index.ts b/apps/web-app/src/features/exchanges/types/index.ts new file mode 100644 index 0000000..1ee1885 --- /dev/null +++ b/apps/web-app/src/features/exchanges/types/index.ts @@ -0,0 +1,89 @@ +export interface ProviderMapping { + id: string; + provider: string; + provider_exchange_code: string; + provider_exchange_name: string; + master_exchange_id: string; + country_code: string | null; + currency: string | null; + confidence: number; + active: boolean; + verified: boolean; + auto_mapped: boolean; + created_at: string; + updated_at: string; + master_exchange_code?: string; + master_exchange_name?: string; + master_exchange_active?: boolean; +} + +export interface Exchange { + id: string; + code: string; + name: string; + country: string; + currency: string; + active: boolean; + created_at: string; + updated_at: string; + provider_mapping_count: string; + active_mapping_count: string; + verified_mapping_count: string; + providers: string | null; +} + +export interface ExchangeDetails { + exchange: Exchange; + provider_mappings: ProviderMapping[]; +} + +export interface ExchangesApiResponse { + success: boolean; + data: Exchange[]; + total: number; +} + +export interface UpdateExchangeRequest { + name?: string; + active?: boolean; + country?: string; + currency?: string; +} + +export interface UpdateProviderMappingRequest { + active?: boolean; + verified?: boolean; + confidence?: number; + master_exchange_id?: string; +} + +export interface CreateProviderMappingRequest { + provider: string; + provider_exchange_code: string; + provider_exchange_name?: string; + master_exchange_id: string; + country_code?: string; + currency?: string; + confidence?: number; + active?: boolean; + verified?: boolean; +} + +export interface ExchangeStats { + total_exchanges: string; + active_exchanges: string; + countries: string; + currencies: string; + total_provider_mappings: string; + active_provider_mappings: string; + verified_provider_mappings: string; + providers: string; +} + +export interface ProviderExchange { + provider_exchange_code: string; + provider_exchange_name: string; + country_code: string | null; + currency: string | null; + symbol_count: number | null; +} diff --git a/apps/web/src/index.css b/apps/web-app/src/index.css similarity index 100% rename from apps/web/src/index.css rename to apps/web-app/src/index.css diff --git a/apps/web/src/lib/constants.ts b/apps/web-app/src/lib/constants.ts similarity index 100% rename from apps/web/src/lib/constants.ts rename to apps/web-app/src/lib/constants.ts diff --git a/apps/web/src/lib/constants/index.ts b/apps/web-app/src/lib/constants/index.ts similarity index 100% rename from apps/web/src/lib/constants/index.ts rename to apps/web-app/src/lib/constants/index.ts diff --git a/apps/web/src/lib/constants/navigation.ts b/apps/web-app/src/lib/constants/navigation.ts similarity index 100% rename from apps/web/src/lib/constants/navigation.ts rename to apps/web-app/src/lib/constants/navigation.ts diff --git a/apps/web/src/lib/utils.ts b/apps/web-app/src/lib/utils.ts similarity index 100% rename from apps/web/src/lib/utils.ts rename to apps/web-app/src/lib/utils.ts diff --git a/apps/web/src/lib/utils/cn.ts b/apps/web-app/src/lib/utils/cn.ts similarity index 100% rename from apps/web/src/lib/utils/cn.ts rename to apps/web-app/src/lib/utils/cn.ts diff --git a/apps/web/src/lib/utils/index.ts b/apps/web-app/src/lib/utils/index.ts similarity index 100% rename from apps/web/src/lib/utils/index.ts rename to apps/web-app/src/lib/utils/index.ts diff --git a/apps/web/src/main.tsx b/apps/web-app/src/main.tsx similarity index 100% rename from apps/web/src/main.tsx rename to apps/web-app/src/main.tsx diff --git a/apps/web/tailwind.config.js b/apps/web-app/tailwind.config.js similarity index 100% rename from apps/web/tailwind.config.js rename to apps/web-app/tailwind.config.js diff --git a/apps/web/tsconfig.json b/apps/web-app/tsconfig.json similarity index 100% rename from apps/web/tsconfig.json rename to apps/web-app/tsconfig.json diff --git a/apps/web/tsconfig.node.json b/apps/web-app/tsconfig.node.json similarity index 100% rename from apps/web/tsconfig.node.json rename to apps/web-app/tsconfig.node.json diff --git a/apps/web/turbo.json b/apps/web-app/turbo.json similarity index 100% rename from apps/web/turbo.json rename to apps/web-app/turbo.json diff --git a/apps/web/vite.config.ts b/apps/web-app/vite.config.ts similarity index 58% rename from apps/web/vite.config.ts rename to apps/web-app/vite.config.ts index 51d3faa..74a5a0b 100644 --- a/apps/web/vite.config.ts +++ b/apps/web-app/vite.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import { fileURLToPath, URL } from 'node:url' +import { fileURLToPath, URL } from 'node:url'; +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; // https://vitejs.dev/config/ export default defineConfig({ @@ -12,6 +12,6 @@ export default defineConfig({ }, server: { port: 3000, - host: true - } -}) + host: true, + }, +}); diff --git a/apps/web/src/features/exchanges/components/ExchangesTable.tsx b/apps/web/src/features/exchanges/components/ExchangesTable.tsx deleted file mode 100644 index eeec310..0000000 --- a/apps/web/src/features/exchanges/components/ExchangesTable.tsx +++ /dev/null @@ -1,283 +0,0 @@ -import { DataTable } from '@/components/ui'; -import { PlusIcon, XMarkIcon } from '@heroicons/react/24/outline'; -import { ColumnDef } from '@tanstack/react-table'; -import { useCallback, useMemo, useState } from 'react'; -import { useExchanges } from '../hooks/useExchanges'; -import { Exchange, SourceMapping } from '../types'; -import { AddSourceDialog } from './AddSourceDialog'; - -export function ExchangesTable() { - const { exchanges, loading, error, updateExchange, addSource, removeSource } = useExchanges(); - const [editingCell, setEditingCell] = useState<{ id: string; field: string } | null>(null); - const [editValue, setEditValue] = useState(''); - const [addSourceDialog, setAddSourceDialog] = useState<{ - id: string; - exchangeName: string; - } | null>(null); - - const handleCellEdit = useCallback( - async (id: string, field: string, value: string) => { - if (field === 'shortName') { - await updateExchange(id, { shortName: value }); - } - setEditingCell(null); - setEditValue(''); - }, - [updateExchange] - ); - - const handleToggleActive = useCallback( - async (id: string, currentStatus: boolean) => { - await updateExchange(id, { active: !currentStatus }); - }, - [updateExchange] - ); - - const handleAddSource = useCallback(async (id: string, exchangeName: string) => { - setAddSourceDialog({ id, exchangeName }); - }, []); - - const handleRemoveSource = useCallback( - async (exchangeId: string, sourceName: string) => { - if (confirm(`Are you sure you want to remove the ${sourceName} source?`)) { - await removeSource(exchangeId, sourceName); - } - }, - [removeSource] - ); - - const columns = useMemo[]>(() => { - return [ - { - id: 'masterExchangeId', - header: 'Master ID', - accessorKey: 'masterExchangeId', - size: 50, - enableResizing: false, - cell: ({ getValue, cell }) => ( - - {getValue() as string} - - ), - }, - { - id: 'shortName', - header: 'Short Name', - accessorKey: 'shortName', - size: 50, - enableResizing: false, - cell: ({ getValue, row, cell }) => { - const isEditing = - editingCell?.id === row.original._id && editingCell?.field === 'shortName'; - - if (isEditing) { - return ( - setEditValue(e.target.value)} - onBlur={() => handleCellEdit(row.original._id, 'shortName', editValue)} - onKeyDown={e => { - if (e.key === 'Enter') { - handleCellEdit(row.original._id, 'shortName', editValue); - } else if (e.key === 'Escape') { - setEditingCell(null); - setEditValue(''); - } - }} - className="w-full bg-surface border border-border rounded px-2 py-1 text-sm" - autoFocus - /> - ); - } - - return ( -
{ - setEditingCell({ id: row.original._id, field: 'shortName' }); - setEditValue(getValue() as string); - }} - > - {getValue() as string} -
- ); - }, - }, - { - id: 'officialName', - header: 'Official Name', - accessorKey: 'officialName', - size: 150, - maxSize: 150, - enableResizing: true, - cell: ({ getValue, cell }) => ( - - {getValue() as string} - - ), - }, - { - id: 'country', - header: 'Country', - accessorKey: 'country', - size: 40, - maxSize: 40, - cell: ({ getValue }) => ( - {getValue() as string} - ), - }, - { - id: 'currency', - header: 'Currency', - accessorKey: 'currency', - size: 40, - cell: ({ getValue, cell }) => ( - - {getValue() as string} - - ), - }, - { - id: 'active', - header: 'Active', - accessorKey: 'active', - size: 80, - maxSize: 80, - cell: ({ getValue, row, cell }) => { - const isActive = getValue() as boolean; - return ( - - ); - }, - }, - { - id: 'sources', - header: 'Sources', - accessorKey: 'sourceMappings', - minSize: 400, - maxSize: 400, - size: 400, - enableResizing: true, - cell: ({ getValue, row, cell }) => { - const sourceMappings = getValue() as Record; - const sources = Object.keys(sourceMappings); - - return ( -
- {sources.map(source => { - // The source key is already in format "source_sourcecode" from the storage - const displayText = source.toUpperCase(); - - return ( - - {displayText} - - - ); - })} - -
- ); - }, - }, - { - id: 'updated_at', - header: 'Last Updated', - accessorKey: 'updated_at', - size: 150, - maxSize: 150, - cell: ({ getValue }) => ( - - {new Date(getValue() as string).toLocaleDateString()} - - ), - }, - ]; - }, [ - editingCell, - editValue, - handleCellEdit, - handleRemoveSource, - handleToggleActive, - handleAddSource, - ]); - - if (error) { - return ( -
-

Error Loading Exchanges

-

{error}

-

- Make sure the data-service is running on localhost:2001 -

-
- ); - } - - return ( - <> - - - {addSourceDialog && ( - setAddSourceDialog(null)} - onAddSource={async (sourceRequest: { - source: string; - source_code: string; - mapping: { id: string; name: string; code: string; aliases: string[] }; - }) => { - const success = await addSource(addSourceDialog.id, sourceRequest); - if (success) { - setAddSourceDialog(null); - } - }} - /> - )} - - ); -} diff --git a/apps/web/src/features/exchanges/hooks/useExchanges.ts b/apps/web/src/features/exchanges/hooks/useExchanges.ts deleted file mode 100644 index d23b7ff..0000000 --- a/apps/web/src/features/exchanges/hooks/useExchanges.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { useCallback, useEffect, useState } from 'react'; -import { AddSourceRequest, Exchange, UpdateExchangeRequest } from '../types'; - -const API_BASE_URL = 'http://localhost:2001/api'; - -export function useExchanges() { - const [exchanges, setExchanges] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const fetchExchanges = useCallback(async () => { - try { - setLoading(true); - setError(null); - - const response = await fetch(`${API_BASE_URL}/exchanges`); - - if (!response.ok) { - throw new Error(`Failed to fetch exchanges: ${response.statusText}`); - } - - const data = await response.json(); - - if (data.status === 'success') { - // The API returns exchanges directly in data.data array - setExchanges(data.data || []); - } else { - throw new Error('API returned error status'); - } - } catch (err) { - console.error('Error fetching exchanges:', err); - setError(err instanceof Error ? err.message : 'Failed to fetch exchanges'); - setExchanges([]); // Reset to empty array on error - } finally { - setLoading(false); - } - }, []); - - const updateExchange = useCallback( - async (id: string, updates: UpdateExchangeRequest) => { - try { - const response = await fetch(`${API_BASE_URL}/exchanges/${id}`, { - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(updates), - }); - - if (!response.ok) { - throw new Error(`Failed to update exchange: ${response.statusText}`); - } - - // Refresh the exchanges list - await fetchExchanges(); - return true; - } catch (err) { - console.error('Error updating exchange:', err); - setError(err instanceof Error ? err.message : 'Failed to update exchange'); - return false; - } - }, - [fetchExchanges] - ); - - const addSource = useCallback( - async (exchangeId: string, sourceRequest: AddSourceRequest) => { - try { - const response = await fetch(`${API_BASE_URL}/exchanges/${exchangeId}/sources`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(sourceRequest), - }); - - if (!response.ok) { - throw new Error(`Failed to add source: ${response.statusText}`); - } - - // Refresh the exchanges list - await fetchExchanges(); - return true; - } catch (err) { - console.error('Error adding source:', err); - setError(err instanceof Error ? err.message : 'Failed to add source'); - return false; - } - }, - [fetchExchanges] - ); - - const removeSource = useCallback( - async (exchangeId: string, sourceName: string) => { - try { - const response = await fetch( - `${API_BASE_URL}/exchanges/${exchangeId}/sources/${sourceName}`, - { - method: 'DELETE', - } - ); - - if (!response.ok) { - throw new Error(`Failed to remove source: ${response.statusText}`); - } - - // Refresh the exchanges list - await fetchExchanges(); - return true; - } catch (err) { - console.error('Error removing source:', err); - setError(err instanceof Error ? err.message : 'Failed to remove source'); - return false; - } - }, - [fetchExchanges] - ); - - const syncExchanges = useCallback(async () => { - try { - const response = await fetch(`${API_BASE_URL}/exchanges/sync`, { - method: 'POST', - }); - - if (!response.ok) { - throw new Error(`Failed to sync exchanges: ${response.statusText}`); - } - - const result = await response.json(); - - // Refresh the exchanges list after sync - await fetchExchanges(); - return result; - } catch (err) { - console.error('Error syncing exchanges:', err); - setError(err instanceof Error ? err.message : 'Failed to sync exchanges'); - return null; - } - }, [fetchExchanges]); - - useEffect(() => { - fetchExchanges(); - }, [fetchExchanges]); - - return { - exchanges, - loading, - error, - refetch: fetchExchanges, - updateExchange, - addSource, - removeSource, - syncExchanges, - }; -} diff --git a/apps/web/src/features/exchanges/types/index.ts b/apps/web/src/features/exchanges/types/index.ts deleted file mode 100644 index d48e58d..0000000 --- a/apps/web/src/features/exchanges/types/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -export interface SourceMapping { - id: string; - name: string; - code: string; - aliases: string[]; - lastUpdated: string; -} - -export interface Exchange { - _id: string; - masterExchangeId: string; - shortName: string; - officialName: string; - country: string; - currency: string; - timezone: string; - active: boolean; - sourceMappings: Record; - confidence: number; - verified: boolean; - source: string; - created_at: string; - updated_at: string; -} - -export interface ExchangesApiResponse { - status: string; - data: Exchange[]; // Exchanges are directly in data array - count: number; -} - -export interface UpdateExchangeRequest { - shortName?: string; - active?: boolean; -} - -export interface AddSourceRequest { - source: string; - source_code: string; - mapping: { - id: string; - name: string; - code: string; - aliases: string[]; - }; -} diff --git a/bun.lock b/bun.lock index a2c89ee..f80a063 100644 --- a/bun.lock +++ b/bun.lock @@ -18,6 +18,7 @@ "devDependencies": { "@eslint/js": "^9.28.0", "@ianvs/prettier-plugin-sort-imports": "^4.4.2", + "@modelcontextprotocol/server-postgres": "^0.6.2", "@testcontainers/mongodb": "^10.7.2", "@testcontainers/postgresql": "^10.7.2", "@types/bun": "latest", @@ -31,6 +32,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^7.2.1", + "mongodb-mcp-server": "^0.1.1", "mongodb-memory-server": "^9.1.6", "pg-mem": "^2.8.1", "prettier": "^3.5.3", @@ -65,6 +67,21 @@ "typescript": "^5.0.0", }, }, + "apps/data-sync-service": { + "name": "@stock-bot/data-sync-service", + "version": "1.0.0", + "dependencies": { + "@stock-bot/config": "*", + "@stock-bot/logger": "*", + "@stock-bot/mongodb-client": "*", + "@stock-bot/postgres-client": "*", + "@stock-bot/shutdown": "*", + "hono": "^4.0.0", + }, + "devDependencies": { + "typescript": "^5.0.0", + }, + }, "apps/execution-service": { "name": "@stock-bot/execution-service", "version": "1.0.0", @@ -138,8 +155,23 @@ "typescript": "^5.0.0", }, }, - "apps/web": { - "name": "@stock-bot/web", + "apps/web-api": { + "name": "@stock-bot/web-api", + "version": "1.0.0", + "dependencies": { + "@stock-bot/config": "*", + "@stock-bot/logger": "*", + "@stock-bot/mongodb-client": "*", + "@stock-bot/postgres-client": "*", + "@stock-bot/shutdown": "*", + "hono": "^4.0.0", + }, + "devDependencies": { + "typescript": "^5.0.0", + }, + }, + "apps/web-app": { + "name": "@stock-bot/web-app", "version": "0.1.0", "dependencies": { "@headlessui/react": "^1.7.17", @@ -467,6 +499,64 @@ "@angular/router": ["@angular/router@20.0.2", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "20.0.2", "@angular/core": "20.0.2", "@angular/platform-browser": "20.0.2", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-UyuTeoXkkZw1eFFNwrTfb1JXow6HKVdLNb3n9MhqDz+3ekdiqDH8EBaKhxYZxlcpNoa6cNbECZJYtaHy1lw38g=="], + "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], + + "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], + + "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="], + + "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], + + "@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-YhhQNVmHykPC6h6Xj60BMG7ELxxlynwNW2wK+8HJRiT62nYhbDyHypY9W2zNshqh/SE+5gLvwt1sXAu7KHGWmQ=="], + + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5zCEpfI+zwX2SIa258L+TItNbBoAvQQ6w74qdFM6YJufQ1F9tvwjTX8T+eSTT9nsFIvfYnUaGalWwJVfmJUgVQ=="], + + "@aws-sdk/core": ["@aws-sdk/core@3.826.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@aws-sdk/xml-builder": "3.821.0", "@smithy/core": "^3.5.3", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "@smithy/util-utf8": "^4.0.0", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-BGbQYzWj3ps+dblq33FY5tz/SsgJCcXX0zjQlSC07tYvU1jHTUvsefphyig+fY38xZ4wdKjbTop+KUmXUYrOXw=="], + + "@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.830.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-YEXmJ1BJ6DzjNnW5OR/5yNPm5d19uifKM6n/1Q1+vooj0OC/zxO9rXo5uQ8Kjs7ZAb0uYSxzy5pTNi5Ilvs8+Q=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.826.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-DK3pQY8+iKK3MGDdC3uOZQ2psU01obaKlTYhEwNu4VWzgwQL4Vi3sWj4xSWGEK41vqZxiRLq6fOq7ysRI+qEZA=="], + + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.826.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/node-http-handler": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-N+IVZBh+yx/9GbMZTKO/gErBi/FYZQtcFRItoLbY+6WU+0cSWyZYfkoeOxHmQV3iX9k65oljERIWUmL9x6OSQg=="], + + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.830.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-env": "3.826.0", "@aws-sdk/credential-provider-http": "3.826.0", "@aws-sdk/credential-provider-process": "3.826.0", "@aws-sdk/credential-provider-sso": "3.830.0", "@aws-sdk/credential-provider-web-identity": "3.830.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-zeQenzvh8JRY5nULd8izdjVGoCM1tgsVVsrLSwDkHxZTTW0hW/bmOmXfvdaE0wDdomXW7m2CkQDSmP7XdvNXZg=="], + + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.830.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.826.0", "@aws-sdk/credential-provider-http": "3.826.0", "@aws-sdk/credential-provider-ini": "3.830.0", "@aws-sdk/credential-provider-process": "3.826.0", "@aws-sdk/credential-provider-sso": "3.830.0", "@aws-sdk/credential-provider-web-identity": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-X/2LrTgwtK1pkWrvofxQBI8VTi6QVLtSMpsKKPPnJQ0vgqC0e4czSIs3ZxiEsOkCBaQ2usXSiKyh0ccsQ6k2OA=="], + + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.826.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-kURrc4amu3NLtw1yZw7EoLNEVhmOMRUTs+chaNcmS+ERm3yK0nKjaJzmKahmwlTQTSl3wJ8jjK7x962VPo+zWw=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.830.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.830.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/token-providers": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-+VdRpZmfekzpySqZikAKx6l5ndnLGluioIgUG4ZznrButgFD/iogzFtGmBDFB3ZLViX1l4pMXru0zFwJEZT21Q=="], + + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.830.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-hPYrKsZeeOdLROJ59T6Y8yZ0iwC/60L3qhZXjapBFjbqBtMaQiMTI645K6xVXBioA6vxXq7B4aLOhYqk6Fy/Ww=="], + + "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.830.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.830.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-cognito-identity": "3.830.0", "@aws-sdk/credential-provider-env": "3.826.0", "@aws-sdk/credential-provider-http": "3.826.0", "@aws-sdk/credential-provider-ini": "3.830.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/credential-provider-process": "3.826.0", "@aws-sdk/credential-provider-sso": "3.830.0", "@aws-sdk/credential-provider-web-identity": "3.830.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-Q16Yf52L9QWsRhaaG/Q6eUkUWGUrbKTM2ba8at8ZZ8tsGaKO5pYgXUTErxB1bin11S6JszinbLqUf9G9oUExxA=="], + + "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw=="], + + "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA=="], + + "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg=="], + + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.828.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@smithy/core": "^3.5.3", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-nixvI/SETXRdmrVab4D9LvXT3lrXkwAWGWk2GVvQvzlqN1/M/RfClj+o37Sn4FqRkGH9o9g7Fqb1YqZ4mqDAtA=="], + + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5N5YTlBr1vtxf7+t+UaIQ625KEAmm7fY9o1e3MgGOi/paBoI0+axr3ud24qLIy0NSzFlAHEaxUSWxcERNjIoZw=="], + + "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" } }, "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw=="], + + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.830.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-aJ4guFwj92nV9D+EgJPaCFKK0I3y2uMchiDfh69Zqnmwfxxxfxat6F79VA7PS0BdbjRfhLbn+Ghjftnomu2c1g=="], + + "@aws-sdk/types": ["@aws-sdk/types@3.821.0", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA=="], + + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.828.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "@smithy/util-endpoints": "^3.0.6", "tslib": "^2.6.2" } }, "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg=="], + + "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.804.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A=="], + + "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw=="], + + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.828.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-LdN6fTBzTlQmc8O8f1wiZN0qF3yBWVGis7NwpWK7FUEzP9bEZRxYfIkV9oV9zpt6iNRze1SedK3JQVB/udxBoA=="], + + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.821.0", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA=="], + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], "@babel/compat-data": ["@babel/compat-data@7.27.5", "", {}, "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg=="], @@ -577,6 +667,16 @@ "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], + "@hapi/boom": ["@hapi/boom@10.0.1", "", { "dependencies": { "@hapi/hoek": "^11.0.2" } }, "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA=="], + + "@hapi/bourne": ["@hapi/bourne@3.0.0", "", {}, "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w=="], + + "@hapi/hoek": ["@hapi/hoek@11.0.7", "", {}, "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ=="], + + "@hapi/topo": ["@hapi/topo@5.1.0", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg=="], + + "@hapi/wreck": ["@hapi/wreck@18.1.0", "", { "dependencies": { "@hapi/boom": "^10.0.1", "@hapi/bourne": "^3.0.0", "@hapi/hoek": "^11.0.2" } }, "sha512-0z6ZRCmFEfV/MQqkQomJ7sl/hyxvcZM7LtuVqN3vdAO4vM9eBbowl0kaqQj9EJJQab+3Uuh1GxbGIBFy4NfJ4w=="], + "@headlessui/react": ["@headlessui/react@1.7.19", "", { "dependencies": { "@tanstack/react-virtual": "^3.0.0-beta.60", "client-only": "^0.0.1" }, "peerDependencies": { "react": "^16 || ^17 || ^18", "react-dom": "^16 || ^17 || ^18" } }, "sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw=="], "@heroicons/react": ["@heroicons/react@2.2.0", "", { "peerDependencies": { "react": ">= 16 || ^19.0.0-rc" } }, "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ=="], @@ -613,8 +713,32 @@ "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.0.1", "", { "dependencies": { "content-type": "^1.0.5", "raw-body": "^3.0.0", "zod": "^3.23.8" } }, "sha512-slLdFaxQJ9AlRg+hw28iiTtGvShAOgOKXcD0F91nUcRYiOMuS9ZBYjcdNZRXW9G5JQ511GRTdUy1zQVZDpJ+4w=="], + + "@modelcontextprotocol/server-postgres": ["@modelcontextprotocol/server-postgres@0.6.2", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.0.1", "pg": "^8.13.0" }, "bin": { "mcp-server-postgres": "dist/index.js" } }, "sha512-ukbVmVxLAsdZ5pTVWbhf9fc7lqkSf7XqizNH8XAotI21GnRPtkqO+WLWpeBFU+/2Fyv63uXS7/9NnR8Y8wOP1Q=="], + + "@mongodb-js/device-id": ["@mongodb-js/device-id@0.2.1", "", {}, "sha512-kC/F1/ryJMNeIt+n7CATAf9AL/X5Nz1Tju8VseyViL2DF640dmF/JQwWmjakpsSTy5X9TVNOkG9ye4Mber8GHQ=="], + + "@mongodb-js/devtools-connect": ["@mongodb-js/devtools-connect@3.8.0", "", { "dependencies": { "@mongodb-js/devtools-proxy-support": "^0.5.0", "@mongodb-js/oidc-http-server-pages": "1.1.4", "lodash.merge": "^4.6.2", "mongodb-connection-string-url": "^3.0.0", "socks": "^2.7.3" }, "optionalDependencies": { "kerberos": "^2.1.0", "mongodb-client-encryption": "^6.1.0", "os-dns-native": "^1.2.0", "resolve-mongodb-srv": "^1.1.1" }, "peerDependencies": { "@mongodb-js/oidc-plugin": "^1.1.0", "mongodb": "^6.9.0", "mongodb-log-writer": "^2.4.1" } }, "sha512-rcl0smpEGjEdCvwv0/RGUMt15PSDqvmYAidnbGzsLWFhClzf4SYdViwUmJNGmo69rS4uCx+r7PMhSoYV+hWwKQ=="], + + "@mongodb-js/devtools-proxy-support": ["@mongodb-js/devtools-proxy-support@0.5.0", "", { "dependencies": { "@mongodb-js/socksv5": "^0.0.10", "agent-base": "^7.1.1", "debug": "^4.4.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.5", "lru-cache": "^11.0.0", "node-fetch": "^3.3.2", "pac-proxy-agent": "^7.0.2", "socks-proxy-agent": "^8.0.4", "ssh2": "^1.15.0", "system-ca": "^2.0.1" } }, "sha512-45vloh7iNanpHZbVGooZq26pjM41iV077Q62m+HmGNMIAkH3U6oswHR1gSPJrEr74F+Wl8KZhpWR7+GaeEFq+Q=="], + + "@mongodb-js/oidc-http-server-pages": ["@mongodb-js/oidc-http-server-pages@1.1.4", "", {}, "sha512-fPwS1cERLGNSz8D1kBw2RJ0GNn1Ud2IIBehvV8OmOZzSXEx6hjwgvKG8XdHT7tpXns7iSkw9gSj84yHJkAlOnQ=="], + + "@mongodb-js/oidc-plugin": ["@mongodb-js/oidc-plugin@1.1.8", "", { "dependencies": { "express": "^4.18.2", "open": "^9.1.0", "openid-client": "^5.6.4" } }, "sha512-83H6SuUm4opxYqEc81AJBXEXlTMO9qnMGXidQFpB2Qwo4MmQtJN4UVm4notqwTBb/ysf410tspUGXy+QLu7xJQ=="], + "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.2.2", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA=="], + "@mongodb-js/socksv5": ["@mongodb-js/socksv5@0.0.10", "", { "dependencies": { "ip-address": "^9.0.5" } }, "sha512-JDz2fLKsjMiSNUxKrCpGptsgu7DzsXfu4gnUQ3RhUaBS1d4YbLrt6HejpckAiHIAa+niBpZAeiUsoop0IihWsw=="], + + "@mongosh/errors": ["@mongosh/errors@2.4.0", "", {}, "sha512-2YwY4TYlrAy3VC9Y5Xa1OWlbdb57O0ZTDfntROFcfotrMXkZc9CU+jafrKRNcPJz8UAhoUcSTDJuaLpC3AutHg=="], + + "@mongosh/service-provider-core": ["@mongosh/service-provider-core@3.3.3", "", { "dependencies": { "@aws-sdk/credential-providers": "^3.525.0", "@mongosh/errors": "2.4.0", "bson": "^6.10.3", "mongodb": "^6.16.0", "mongodb-build-info": "^1.7.2", "mongodb-connection-string-url": "^3.0.1" }, "optionalDependencies": { "mongodb-client-encryption": "^6.3.0" } }, "sha512-Cylm0JjY0iu2C91o3koGNDtx7WhhFhCo+zWSxD5+aFiuAxrQQEmVxqLGFB9QTHwUotsdk2i7zi2lMdYVtCnkCA=="], + + "@mongosh/service-provider-node-driver": ["@mongosh/service-provider-node-driver@3.9.0", "", { "dependencies": { "@mongodb-js/devtools-connect": "^3.4.1", "@mongodb-js/oidc-plugin": "^1.1.7", "@mongosh/errors": "2.4.0", "@mongosh/service-provider-core": "3.3.3", "@mongosh/types": "3.7.0", "aws4": "^1.12.0", "mongodb": "^6.16.0", "mongodb-connection-string-url": "^3.0.1", "socks": "^2.8.3" }, "optionalDependencies": { "kerberos": "2.1.0", "mongodb-client-encryption": "^6.3.0" } }, "sha512-90SWQstmt4erFbgkb/2jEoO12LyzL9gIPmsX1Uhrnbb1TgkQQD45Ek8+vaXH0g1+4FXSTrkvButajnbmUnc0kA=="], + + "@mongosh/types": ["@mongosh/types@3.7.0", "", { "dependencies": { "@mongodb-js/devtools-connect": "^3.4.1" } }, "sha512-2eo5y5GlYz/vbz0E/00rBY+xj9Gw4VIq7fe/KAkva3Os1nsFUkEWUMKBA2oSfdbYEQmxia/huVwhBElqmHqmZw=="], + "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], @@ -671,8 +795,92 @@ "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], + "@sideway/address": ["@sideway/address@4.1.5", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q=="], + + "@sideway/formula": ["@sideway/formula@3.0.1", "", {}, "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="], + + "@sideway/pinpoint": ["@sideway/pinpoint@2.0.0", "", {}, "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="], + "@sindresorhus/is": ["@sindresorhus/is@7.0.2", "", {}, "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw=="], + "@smithy/abort-controller": ["@smithy/abort-controller@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA=="], + + "@smithy/config-resolver": ["@smithy/config-resolver@4.1.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" } }, "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w=="], + + "@smithy/core": ["@smithy/core@3.5.3", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.8", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA=="], + + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.0.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "tslib": "^2.6.2" } }, "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw=="], + + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.4", "", { "dependencies": { "@smithy/protocol-http": "^5.1.2", "@smithy/querystring-builder": "^4.0.4", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw=="], + + "@smithy/hash-node": ["@smithy/hash-node@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ=="], + + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw=="], + + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], + + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.0.4", "", { "dependencies": { "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w=="], + + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.1.11", "", { "dependencies": { "@smithy/core": "^3.5.3", "@smithy/middleware-serde": "^4.0.8", "@smithy/node-config-provider": "^4.1.3", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" } }, "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ=="], + + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.1.12", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/protocol-http": "^5.1.2", "@smithy/service-error-classification": "^4.0.5", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww=="], + + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.8", "", { "dependencies": { "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw=="], + + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA=="], + + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.1.3", "", { "dependencies": { "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw=="], + + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.6", "", { "dependencies": { "@smithy/abort-controller": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/querystring-builder": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA=="], + + "@smithy/property-provider": ["@smithy/property-provider@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw=="], + + "@smithy/protocol-http": ["@smithy/protocol-http@5.1.2", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ=="], + + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w=="], + + "@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w=="], + + "@smithy/service-error-classification": ["@smithy/service-error-classification@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.1" } }, "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA=="], + + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw=="], + + "@smithy/signature-v4": ["@smithy/signature-v4@5.1.2", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "@smithy/util-uri-escape": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ=="], + + "@smithy/smithy-client": ["@smithy/smithy-client@4.4.3", "", { "dependencies": { "@smithy/core": "^3.5.3", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-stack": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA=="], + + "@smithy/types": ["@smithy/types@4.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA=="], + + "@smithy/url-parser": ["@smithy/url-parser@4.0.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ=="], + + "@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="], + + "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="], + + "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg=="], + + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], + + "@smithy/util-config-provider": ["@smithy/util-config-provider@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w=="], + + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.0.19", "", { "dependencies": { "@smithy/property-provider": "^4.0.4", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ=="], + + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.0.19", "", { "dependencies": { "@smithy/config-resolver": "^4.1.4", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w=="], + + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.0.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA=="], + + "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw=="], + + "@smithy/util-middleware": ["@smithy/util-middleware@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ=="], + + "@smithy/util-retry": ["@smithy/util-retry@4.0.5", "", { "dependencies": { "@smithy/service-error-classification": "^4.0.5", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg=="], + + "@smithy/util-stream": ["@smithy/util-stream@4.2.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.4", "@smithy/node-http-handler": "^4.0.6", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w=="], + + "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg=="], + + "@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="], + "@stock-bot/browser": ["@stock-bot/browser@workspace:libs/browser"], "@stock-bot/cache": ["@stock-bot/cache@workspace:libs/cache"], @@ -685,6 +893,8 @@ "@stock-bot/data-service": ["@stock-bot/data-service@workspace:apps/data-service"], + "@stock-bot/data-sync-service": ["@stock-bot/data-sync-service@workspace:apps/data-sync-service"], + "@stock-bot/event-bus": ["@stock-bot/event-bus@workspace:libs/event-bus"], "@stock-bot/execution-service": ["@stock-bot/execution-service@workspace:apps/execution-service"], @@ -719,7 +929,9 @@ "@stock-bot/vector-engine": ["@stock-bot/vector-engine@workspace:libs/vector-engine"], - "@stock-bot/web": ["@stock-bot/web@workspace:apps/web"], + "@stock-bot/web-api": ["@stock-bot/web-api@workspace:apps/web-api"], + + "@stock-bot/web-app": ["@stock-bot/web-app@workspace:apps/web-app"], "@szmarczak/http-timer": ["@szmarczak/http-timer@5.0.1", "", { "dependencies": { "defer-to-connect": "^2.0.1" } }, "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw=="], @@ -737,6 +949,8 @@ "@testcontainers/postgresql": ["@testcontainers/postgresql@10.28.0", "", { "dependencies": { "testcontainers": "^10.28.0" } }, "sha512-NN25rruG5D4Q7pCNIJuHwB+G85OSeJ3xHZ2fWx0O6sPoPEfCYwvpj8mq99cyn68nxFkFYZeyrZJtSFO+FnydiA=="], + "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="], + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], @@ -839,6 +1053,8 @@ "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -865,6 +1081,8 @@ "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], @@ -885,6 +1103,8 @@ "asn1": ["asn1@0.2.6", "", { "dependencies": { "safer-buffer": "~2.1.0" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="], + "ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="], + "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], @@ -901,6 +1121,8 @@ "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + "aws4": ["aws4@1.13.2", "", {}, "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="], + "axios": ["axios@1.9.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg=="], "b4a": ["b4a@1.6.7", "", {}, "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg=="], @@ -919,14 +1141,26 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "basic-ftp": ["basic-ftp@5.0.5", "", {}, "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg=="], + "bcrypt-pbkdf": ["bcrypt-pbkdf@1.0.2", "", { "dependencies": { "tweetnacl": "^0.14.3" } }, "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w=="], + "big-integer": ["big-integer@1.6.52", "", {}, "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="], + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], + "boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="], + "bowser": ["bowser@2.11.0", "", {}, "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="], + + "bplist-parser": ["bplist-parser@0.2.0", "", { "dependencies": { "big-integer": "^1.6.44" } }, "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw=="], + "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=="], @@ -945,8 +1179,12 @@ "bun-types": ["bun-types@1.2.15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="], + "bundle-name": ["bundle-name@3.0.0", "", { "dependencies": { "run-applescript": "^5.0.0" } }, "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw=="], + "byline": ["byline@5.0.0", "", {}, "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "cacheable-lookup": ["cacheable-lookup@7.0.0", "", {}, "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w=="], "cacheable-request": ["cacheable-request@12.0.1", "", { "dependencies": { "@types/http-cache-semantics": "^4.0.4", "get-stream": "^9.0.1", "http-cache-semantics": "^4.1.1", "keyv": "^4.5.4", "mimic-response": "^4.0.0", "normalize-url": "^8.0.1", "responselike": "^3.0.0" } }, "sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg=="], @@ -973,6 +1211,8 @@ "chromium-bidi": ["chromium-bidi@5.3.1", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-fbkgn0/m6RIRknVEez+QOYuvukUomBC0XnS8fgdbl9FeunjR3vUvPN6iYrbzXIuaJXYOwGU8FZgOTDzBImGvLw=="], + "cli-table": ["cli-table@0.3.11", "", { "dependencies": { "colors": "1.0.3" } }, "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ=="], + "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=="], @@ -989,6 +1229,8 @@ "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], + "colors": ["colors@1.0.3", "", {}, "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw=="], + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], "commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], @@ -1001,14 +1243,22 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + "cookiejar": ["cookiejar@2.1.4", "", {}, "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw=="], "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="], + "cpu-features": ["cpu-features@0.0.10", "", { "dependencies": { "buildcheck": "~0.0.6", "nan": "^2.19.0" } }, "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA=="], "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="], @@ -1023,6 +1273,8 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], @@ -1037,18 +1289,32 @@ "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + "default-browser": ["default-browser@4.0.0", "", { "dependencies": { "bundle-name": "^3.0.0", "default-browser-id": "^3.0.0", "execa": "^7.1.1", "titleize": "^3.0.0" } }, "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA=="], + + "default-browser-id": ["default-browser-id@3.0.0", "", { "dependencies": { "bplist-parser": "^0.2.0", "untildify": "^4.0.0" } }, "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA=="], + "defer-to-connect": ["defer-to-connect@2.0.1", "", {}, "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="], "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + "degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="], + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], + "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], "detect-node": ["detect-node@2.1.0", "", {}, "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="], @@ -1079,12 +1345,16 @@ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "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=="], + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="], "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], @@ -1113,8 +1383,12 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="], + "eslint": ["eslint@9.28.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.1", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.28.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ=="], "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], @@ -1143,6 +1417,8 @@ "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], @@ -1151,12 +1427,26 @@ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.0.2", "", {}, "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA=="], + + "execa": ["execa@7.2.0", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.1", "human-signals": "^4.3.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^3.0.7", "strip-final-newline": "^3.0.0" } }, "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA=="], + + "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + + "express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], + + "express-rate-limit": ["express-rate-limit@7.5.0", "", { "peerDependencies": { "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg=="], + "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=="], @@ -1175,14 +1465,22 @@ "fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="], + "fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="], + "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=="], + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], + "find-cache-dir": ["find-cache-dir@3.3.2", "", { "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", "pkg-dir": "^4.1.0" } }, "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig=="], "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], @@ -1201,10 +1499,16 @@ "form-data-encoder": ["form-data-encoder@4.1.0", "", {}, "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw=="], + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + "formidable": ["formidable@2.1.5", "", { "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", "once": "^1.4.0", "qs": "^6.11.0" } }, "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + "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=="], @@ -1235,6 +1539,10 @@ "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=="], + "get-uri": ["get-uri@6.0.4", "", { "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" } }, "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ=="], + + "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], @@ -1269,18 +1577,26 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "heap-js": ["heap-js@2.6.0", "", {}, "sha512-trFMIq3PATiFRiQmNNeHtsrkwYRByIXUbYNbotiY9RLVfMkdwZdd2eQ38mGt7BRiCKBaj1DyBAIHmm7mmXPuuw=="], + "help-me": ["help-me@5.0.0", "", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="], "hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="], "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], "http2-wrapper": ["http2-wrapper@2.2.1", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.2.0" } }, "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ=="], "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "human-signals": ["human-signals@4.3.1", "", {}, "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], @@ -1295,12 +1611,18 @@ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], "ioredis": ["ioredis@5.6.1", "", { "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA=="], "ip-address": ["ip-address@9.0.5", "", { "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" } }, "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g=="], + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "ipv6-normalize": ["ipv6-normalize@1.0.1", "", {}, "sha512-Bm6H79i01DjgGTCWjUuCjJ6QDo1HB96PT/xCYuyJUP9WFbVDrLSbG4EZCvOCun2rNswZb0c3e4Jt/ws795esHA=="], + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], @@ -1319,6 +1641,8 @@ "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], @@ -1329,6 +1653,8 @@ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], @@ -1339,6 +1665,8 @@ "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], @@ -1359,16 +1687,24 @@ "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + "is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "isnumber": ["isnumber@1.0.0", "", {}, "sha512-JLiSz/zsZcGFXPrB4I/AGBvtStkt+8QmksyZBZnVXnnK9XdTEyz0tX8CRYljtwYDuIuZzih6DpHQdi+3Q6zHPw=="], + "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], + "joi": ["joi@17.13.3", "", { "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA=="], + + "jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="], + "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], @@ -1397,6 +1733,8 @@ "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=="], + "kerberos": ["kerberos@2.1.0", "", { "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^6.1.0", "prebuild-install": "7.1.1" } }, "sha512-HvOl6O6cyEN/8Z4CAocHe/sekJtvt5UrxUdCuu7bXDZ2Hnsy6OpsQbISW+lpm03vrbO2ir+1QQ5Sx/vMEhHnog=="], + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], "lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "^2.0.5" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="], @@ -1427,20 +1765,28 @@ "lowercase-keys": ["lowercase-keys@3.0.0", "", {}, "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="], - "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + "lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="], "luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="], + "macos-export-certificate-and-key": ["macos-export-certificate-and-key@1.2.4", "", { "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^4.3.0" }, "os": "darwin" }, "sha512-y5QZEywlBNKd+EhPZ1Hz1FmDbbeQKtuVHJaTlawdl7vXw9bi/4tJB2xSMwX4sMVcddy3gbQ8K0IqXAi2TpDo2g=="], + "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=="], + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + "memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="], "memory-pager": ["memory-pager@1.5.0", "", {}, "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="], + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], @@ -1453,6 +1799,8 @@ "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="], + "mimic-response": ["mimic-response@4.0.0", "", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="], "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], @@ -1471,12 +1819,26 @@ "mongodb": ["mongodb@6.17.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.1.9", "bson": "^6.10.4", "mongodb-connection-string-url": "^3.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.2.2", "socks": "^2.7.1" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA=="], + "mongodb-build-info": ["mongodb-build-info@1.7.2", "", { "dependencies": { "mongodb-connection-string-url": "^3.0.0" } }, "sha512-eoLFZvCIjcwijYJdxvYupj1c+55VAVm0o4gBJjrcDxxmmpm+bC4Ix9ayZbyhQdVXDZAGDi03NA0GghXjBVXnxg=="], + + "mongodb-client-encryption": ["mongodb-client-encryption@6.4.0", "", { "dependencies": { "node-addon-api": "^4.3.0", "prebuild-install": "^7.1.3" } }, "sha512-Un1W/5P4KjcUBPeJeSKFNaWH0/8PVsoSatDqyWM2bMK0Vu2Jjxy7ZTgDj1g+uChuqroB09s8LvppdsHpwxSTVA=="], + "mongodb-connection-string-url": ["mongodb-connection-string-url@3.0.2", "", { "dependencies": { "@types/whatwg-url": "^11.0.2", "whatwg-url": "^14.1.0 || ^13.0.0" } }, "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA=="], + "mongodb-log-writer": ["mongodb-log-writer@2.4.1", "", { "dependencies": { "heap-js": "^2.3.0" }, "peerDependencies": { "bson": "6.x" } }, "sha512-kTVWtiUbayr2S54WeOeHpXvR80ASwlmoMsA3LIxH+PVZle8ddq7cXJXM3O5kkuT+Uni9+YNOTBwoRYVQlIAEUQ=="], + + "mongodb-mcp-server": ["mongodb-mcp-server@0.1.1", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.8.0", "@mongodb-js/device-id": "^0.2.1", "@mongodb-js/devtools-connect": "^3.7.2", "@mongosh/service-provider-node-driver": "^3.6.0", "bson": "^6.10.3", "lru-cache": "^11.1.0", "mongodb": "^6.15.0", "mongodb-connection-string-url": "^3.0.2", "mongodb-log-writer": "^2.4.1", "mongodb-redact": "^1.1.6", "mongodb-schema": "^12.6.2", "node-machine-id": "1.1.12", "openapi-fetch": "^0.13.5", "simple-oauth2": "^5.1.0", "yargs-parser": "^21.1.1", "zod": "^3.24.2" }, "bin": { "mongodb-mcp-server": "dist/index.js" } }, "sha512-Ajyj4h3PYhGAwTXiOVrmFAIJ8xozSChdk1FgcI33UtGsdYaRkVA+hCswIVZp+ZBh8BJvIL8JvV3wM56hesppQQ=="], + "mongodb-memory-server": ["mongodb-memory-server@9.5.0", "", { "dependencies": { "mongodb-memory-server-core": "9.5.0", "tslib": "^2.6.3" } }, "sha512-In3zRT40cLlVtpy7FK6b96Lby6JBAdXj8Kf9YrH4p1Aa2X4ptojq7SmiRR3x47Lo0/UCXXIwhJpkdbYY8kRZAw=="], "mongodb-memory-server-core": ["mongodb-memory-server-core@9.5.0", "", { "dependencies": { "async-mutex": "^0.4.1", "camelcase": "^6.3.0", "debug": "^4.3.7", "find-cache-dir": "^3.3.2", "follow-redirects": "^1.15.9", "https-proxy-agent": "^7.0.5", "mongodb": "^5.9.2", "new-find-package-json": "^2.0.0", "semver": "^7.6.3", "tar-stream": "^3.1.7", "tslib": "^2.6.3", "yauzl": "^3.1.3" } }, "sha512-Jb/V80JeYAKWaF4bPFme7SmTR6ew1PWgkpPUepLDfRraeN49i1cruxICeA4zz4T33W/o31N+zazP8wI8ebf7yw=="], + "mongodb-ns": ["mongodb-ns@2.4.2", "", {}, "sha512-gYJjEYG4v4a1WSXgUf81OBoBRlj+Z1SlnQVO392fC/4a1CN7CLWDITajZWPFTPh/yRozYk6sHHtZwZmQhodBEA=="], + + "mongodb-redact": ["mongodb-redact@1.1.6", "", {}, "sha512-L4L3byUH/V/L6YH954NBM/zJpyDHQYmm9eUCxMxqMUfiYCPtmCK1sv/LhxE7UonOkFNEAT6eq2J8gIWGUpHcJA=="], + + "mongodb-schema": ["mongodb-schema@12.6.2", "", { "dependencies": { "reservoir": "^0.1.2" }, "optionalDependencies": { "bson": "^6.7.0", "cli-table": "^0.3.4", "js-yaml": "^4.0.0", "mongodb": "^6.6.1", "mongodb-ns": "^2.4.0", "numeral": "^2.0.6", "progress": "^2.0.3", "stats-lite": "^2.0.0", "yargs": "^17.6.2" }, "bin": { "mongodb-schema": "bin/mongodb-schema" } }, "sha512-uKjkTAx6MqJi0Xj0aeYRjvYr3O7LrUQgXH1c0WQCOByPoYbNG9RAhWoc4IwriIqTHyBw1RJn0C/p7DISOPYpMg=="], + "moo": ["moo@0.5.2", "", {}, "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -1491,16 +1853,32 @@ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "napi-build-utils": ["napi-build-utils@1.0.2", "", {}, "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="], + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], "nearley": ["nearley@2.20.1", "", { "dependencies": { "commander": "^2.19.0", "moo": "^0.5.0", "railroad-diagrams": "^1.0.0", "randexp": "0.4.6" }, "bin": { "nearleyc": "bin/nearleyc.js", "nearley-test": "bin/nearley-test.js", "nearley-unparse": "bin/nearley-unparse.js", "nearley-railroad": "bin/nearley-railroad.js" } }, "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ=="], + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="], + "new-find-package-json": ["new-find-package-json@2.0.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew=="], + "node-abi": ["node-abi@3.75.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg=="], + "node-abort-controller": ["node-abort-controller@3.1.1", "", {}, "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="], + "node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="], + + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + + "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], + "node-machine-id": ["node-machine-id@1.1.12", "", {}, "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ=="], + "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], @@ -1509,6 +1887,10 @@ "normalize-url": ["normalize-url@8.0.2", "", {}, "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw=="], + "npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="], + + "numeral": ["numeral@2.0.6", "", {}, "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], "object-hash": ["object-hash@2.2.0", "", {}, "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="], @@ -1527,12 +1909,28 @@ "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + "oidc-token-hash": ["oidc-token-hash@5.1.0", "", {}, "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA=="], + "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="], + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="], + + "open": ["open@9.1.0", "", { "dependencies": { "default-browser": "^4.0.0", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^2.2.0" } }, "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg=="], + + "openapi-fetch": ["openapi-fetch@0.13.8", "", { "dependencies": { "openapi-typescript-helpers": "^0.0.15" } }, "sha512-yJ4QKRyNxE44baQ9mY5+r/kAzZ8yXMemtNAOFwOzRXJscdjSxxzWSNlyBAr+o5JjkUw9Lc3W7OIoca0cY3PYnQ=="], + + "openapi-typescript-helpers": ["openapi-typescript-helpers@0.0.15", "", {}, "sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw=="], + + "openid-client": ["openid-client@5.7.1", "", { "dependencies": { "jose": "^4.15.9", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew=="], + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + "os-dns-native": ["os-dns-native@1.2.1", "", { "dependencies": { "bindings": "^1.5.0", "debug": "^4.3.3", "ipv6-normalize": "^1.0.1", "node-addon-api": "^4.3.0" } }, "sha512-LbU43lWBxnZhy72Ngr+Vga0og5Q2+Ob8lvSHJkP2uYBkvdmAnK4CvaVaBhC1hk9AQV3YxAZ9fZWaJTuIyPEi+Q=="], + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], "p-cancelable": ["p-cancelable@4.0.1", "", {}, "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg=="], @@ -1543,12 +1941,18 @@ "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], + "pac-proxy-agent": ["pac-proxy-agent@7.2.0", "", { "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", "socks-proxy-agent": "^8.0.5" } }, "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA=="], + + "pac-resolver": ["pac-resolver@7.0.1", "", { "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" } }, "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg=="], + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], @@ -1559,6 +1963,8 @@ "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], @@ -1601,6 +2007,8 @@ "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], + "pkce-challenge": ["pkce-challenge@5.0.0", "", {}, "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ=="], + "pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="], "playwright": ["playwright@1.53.0", "", { "dependencies": { "playwright-core": "1.53.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-ghGNnIEYZC4E+YtclRn4/p6oYbdPiASELBIYkBXfaTVKreQUYbMUYQDwS12a8F0/HtIjr/CkGjtwABeFPGcS4Q=="], @@ -1631,6 +2039,8 @@ "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + "prebuild-install": ["prebuild-install@7.1.1", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^1.0.1", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw=="], + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="], @@ -1657,6 +2067,8 @@ "protobufjs": ["protobufjs@7.5.3", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], "pump": ["pump@3.0.2", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw=="], @@ -1675,6 +2087,12 @@ "randexp": ["randexp@0.4.6", "", { "dependencies": { "discontinuous-range": "1.0.0", "ret": "~0.1.10" } }, "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ=="], + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="], + + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], "react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="], @@ -1717,12 +2135,16 @@ "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + "reservoir": ["reservoir@0.1.2", "", {}, "sha512-ysyw95gLBhMAzqIVrOHJ2yMrRQHAS+h97bS9r89Z7Ou10Jhl2k5KOsyjPqrxL+WfEanov0o5bAMVzQ7AKyENHA=="], + "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], "resolve-alpn": ["resolve-alpn@1.2.1", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="], "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "resolve-mongodb-srv": ["resolve-mongodb-srv@1.1.5", "", { "dependencies": { "whatwg-url": "^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0" }, "bin": { "resolve-mongodb-srv": "bin/resolve-mongodb-srv.js" } }, "sha512-flu1XTSLDJHvTnWu2aJh2w9jgGPcNYJn2obMkuzXiyWSz0MLXu9IRCjvirJ4zRoCPHJJPt3uLQVNJTrzFRWd1w=="], + "responselike": ["responselike@3.0.0", "", { "dependencies": { "lowercase-keys": "^3.0.0" } }, "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg=="], "ret": ["ret@0.1.15", "", {}, "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="], @@ -1737,6 +2159,10 @@ "rollup": ["rollup@3.29.5", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w=="], + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "run-applescript": ["run-applescript@5.0.0", "", { "dependencies": { "execa": "^5.0.0" } }, "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg=="], + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], @@ -1761,8 +2187,12 @@ "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], + "send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], + "serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="], + "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="], + "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=="], @@ -1771,6 +2201,8 @@ "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], @@ -1785,6 +2217,12 @@ "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], + + "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], + + "simple-oauth2": ["simple-oauth2@5.1.0", "", { "dependencies": { "@hapi/hoek": "^11.0.4", "@hapi/wreck": "^18.0.0", "debug": "^4.3.4", "joi": "^17.6.4" } }, "sha512-gWDa38Ccm4MwlG5U7AlcJxPv3lvr80dU7ARJWrGdgvOKyzSj1gr3GBPN1rABTedAYvC/LsGYoFuFxwDBPtGEbw=="], + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], @@ -1795,6 +2233,8 @@ "sonic-boom": ["sonic-boom@4.2.0", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww=="], + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "sparse-bitfield": ["sparse-bitfield@3.0.3", "", { "dependencies": { "memory-pager": "^1.0.2" } }, "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ=="], @@ -1811,6 +2251,10 @@ "standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="], + "stats-lite": ["stats-lite@2.2.0", "", { "dependencies": { "isnumber": "~1.0.0" } }, "sha512-/Kz55rgUIv2KP2MKphwYT/NCuSfAlbbMRv2ZWw7wyXayu230zdtzhxxuXXcvsc6EmmhS8bSJl3uS1wmMHFumbA=="], + + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], "streamx": ["streamx@2.22.1", "", { "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" } }, "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA=="], @@ -1837,8 +2281,12 @@ "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + "strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + "strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="], + "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=="], @@ -1851,6 +2299,8 @@ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + "system-ca": ["system-ca@2.0.1", "", { "optionalDependencies": { "macos-export-certificate-and-key": "^1.2.0", "win-export-certificate-and-key": "^2.1.0" } }, "sha512-9ZDV9yl8ph6Op67wDGPr4LykX86usE9x3le+XZSHfVMiiVJ5IRgmCWjLgxyz35ju9H3GDIJJZm4ogAeIfN5cQQ=="], + "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], "tailwindcss": ["tailwindcss@3.4.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.6", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og=="], @@ -1873,10 +2323,14 @@ "tiny-case": ["tiny-case@1.0.3", "", {}, "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="], + "titleize": ["titleize@3.0.0", "", {}, "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ=="], + "tmp": ["tmp@0.2.3", "", {}, "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "toposort": ["toposort@2.0.2", "", {}, "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="], "tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], @@ -1889,6 +2343,8 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], + "turbo": ["turbo@2.5.4", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.4", "turbo-darwin-arm64": "2.5.4", "turbo-linux-64": "2.5.4", "turbo-linux-arm64": "2.5.4", "turbo-windows-64": "2.5.4", "turbo-windows-arm64": "2.5.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-kc8ZibdRcuWUG1pbYSBFWqmIjynlD8Lp7IB6U3vIzvOv9VG+6Sp8bzyeBWE3Oi8XV5KsQrznyRTBPvrf99E4mA=="], "turbo-darwin-64": ["turbo-darwin-64@2.5.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ah6YnH2dErojhFooxEzmvsoZQTMImaruZhFPfMKPBq8sb+hALRdvBNLqfc8NWlZq576FkfRZ/MSi4SHvVFT9PQ=="], @@ -1909,6 +2365,8 @@ "type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], @@ -1927,6 +2385,10 @@ "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "untildify": ["untildify@4.0.0", "", {}, "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw=="], + "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=="], @@ -1935,10 +2397,16 @@ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], + "uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "vite": ["vite@4.5.14", "", { "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", "rollup": "^3.27.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g=="], + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], "whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], @@ -1953,9 +2421,11 @@ "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], + "win-export-certificate-and-key": ["win-export-certificate-and-key@2.1.0", "", { "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^3.1.0" }, "os": "win32" }, "sha512-WeMLa/2uNZcS/HWGKU2G1Gzeh3vHpV/UFvwLhJLKxPHYFAbubxxVcJbqmPXaqySWK1Ymymh16zKK5WYIJ3zgzA=="], + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + "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=="], "wrap-ansi-cjs": ["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=="], @@ -1985,8 +2455,14 @@ "zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="], + "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], + "zone.js": ["zone.js@0.15.1", "", {}, "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w=="], + "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + + "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -2007,12 +2483,20 @@ "@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "@hapi/topo/@hapi/hoek": ["@hapi/hoek@9.3.0", "", {}, "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="], + "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@mongodb-js/oidc-plugin/express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="], + + "@sideway/address/@hapi/hoek": ["@hapi/hoek@9.3.0", "", {}, "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="], + "@stock-bot/browser/@types/node": ["@types/node@20.19.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q=="], "@stock-bot/cache/@types/node": ["@types/node@20.19.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q=="], @@ -2081,28 +2565,30 @@ "@stock-bot/vector-engine/@types/node": ["@types/node@20.19.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@6.21.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/type-utils": "6.21.0", "@typescript-eslint/utils": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@6.21.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/type-utils": "6.21.0", "@typescript-eslint/utils": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA=="], - "@stock-bot/web/@typescript-eslint/parser": ["@typescript-eslint/parser@6.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="], + "@stock-bot/web-app/@typescript-eslint/parser": ["@typescript-eslint/parser@6.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="], - "@stock-bot/web/eslint": ["eslint@8.57.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.1", "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA=="], + "@stock-bot/web-app/eslint": ["eslint@8.57.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.1", "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "accepts/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + "archiver-utils/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "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=="], + "body-parser/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + "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=="], @@ -2135,12 +2621,24 @@ "eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@1.3.0", "", {}, "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="], + "execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="], + + "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "express/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + + "express/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + "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=="], + "get-uri/data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="], + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "globals/type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], @@ -2149,26 +2647,54 @@ "got/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "is-wsl/is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], + + "joi/@hapi/hoek": ["@hapi/hoek@9.3.0", "", {}, "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="], + "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "macos-export-certificate-and-key/node-addon-api": ["node-addon-api@4.3.0", "", {}, "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="], + "make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "mongodb-client-encryption/node-addon-api": ["node-addon-api@4.3.0", "", {}, "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="], + + "mongodb-client-encryption/prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], + + "mongodb-mcp-server/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.12.3", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-DyVYSOafBvk3/j1Oka4z5BWT8o4AFmoNyZY9pALOm7Lh3GZglR71Co4r4dEUoqDWdDazIZQHBe7J2Nwkg6gHgQ=="], + "mongodb-memory-server-core/mongodb": ["mongodb@5.9.2", "", { "dependencies": { "bson": "^5.5.0", "mongodb-connection-string-url": "^2.6.0", "socks": "^2.7.1" }, "optionalDependencies": { "@mongodb-js/saslprep": "^1.1.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.0.0", "kerberos": "^1.0.0 || ^2.0.0", "mongodb-client-encryption": ">=2.3.0 <3", "snappy": "^7.2.2" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "kerberos", "mongodb-client-encryption", "snappy"] }, "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ=="], "nearley/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + + "openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + + "os-dns-native/node-addon-api": ["node-addon-api@4.3.0", "", {}, "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="], + "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "pg-mem/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + "pkg-dir/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], + "prebuild-install/tar-fs": ["tar-fs@2.1.3", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg=="], + + "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "readdir-glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], "rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "run-applescript/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + + "send/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + "serialize-error/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="], "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], @@ -2177,16 +2703,18 @@ "tailwindcss/object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], + "type-is/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], - - "wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], - - "wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "win-export-certificate-and-key/node-addon-api": ["node-addon-api@3.2.1", "", {}, "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="], "yauzl/buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + "@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=="], @@ -2209,6 +2737,34 @@ "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "@mongodb-js/oidc-plugin/express/accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + + "@mongodb-js/oidc-plugin/express/body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g=="], + + "@mongodb-js/oidc-plugin/express/content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], + + "@mongodb-js/oidc-plugin/express/cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="], + + "@mongodb-js/oidc-plugin/express/cookie-signature": ["cookie-signature@1.0.6", "", {}, "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="], + + "@mongodb-js/oidc-plugin/express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "@mongodb-js/oidc-plugin/express/finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ=="], + + "@mongodb-js/oidc-plugin/express/fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + + "@mongodb-js/oidc-plugin/express/merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], + + "@mongodb-js/oidc-plugin/express/path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="], + + "@mongodb-js/oidc-plugin/express/send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="], + + "@mongodb-js/oidc-plugin/express/serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="], + + "@mongodb-js/oidc-plugin/express/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + "@stock-bot/config/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="], "@stock-bot/config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@6.21.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag=="], @@ -2389,46 +2945,50 @@ "@stock-bot/questdb-client/eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@6.21.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@6.21.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@6.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@6.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" } }, "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" } }, "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], - "@stock-bot/web/@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="], + "@stock-bot/web-app/@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="], - "@stock-bot/web/@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], + "@stock-bot/web-app/@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], - "@stock-bot/web/@typescript-eslint/parser/@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=="], + "@stock-bot/web-app/@typescript-eslint/parser/@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=="], - "@stock-bot/web/@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" } }, "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="], + "@stock-bot/web-app/@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" } }, "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="], - "@stock-bot/web/eslint/@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], + "@stock-bot/web-app/eslint/@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], - "@stock-bot/web/eslint/@eslint/js": ["@eslint/js@8.57.1", "", {}, "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="], + "@stock-bot/web-app/eslint/@eslint/js": ["@eslint/js@8.57.1", "", {}, "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="], - "@stock-bot/web/eslint/doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], + "@stock-bot/web-app/eslint/doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], - "@stock-bot/web/eslint/eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], + "@stock-bot/web-app/eslint/eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], - "@stock-bot/web/eslint/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + "@stock-bot/web-app/eslint/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - "@stock-bot/web/eslint/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + "@stock-bot/web-app/eslint/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], - "@stock-bot/web/eslint/file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="], + "@stock-bot/web-app/eslint/file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="], - "@stock-bot/web/eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "@stock-bot/web-app/eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "accepts/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + "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=="], + "express/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + "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=="], @@ -2439,6 +2999,10 @@ "lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "mongodb-client-encryption/prebuild-install/napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], + + "mongodb-client-encryption/prebuild-install/tar-fs": ["tar-fs@2.1.3", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg=="], + "mongodb-memory-server-core/mongodb/bson": ["bson@5.5.1", "", {}, "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g=="], "mongodb-memory-server-core/mongodb/mongodb-connection-string-url": ["mongodb-connection-string-url@2.6.0", "", { "dependencies": { "@types/whatwg-url": "^8.2.1", "whatwg-url": "^11.0.0" } }, "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ=="], @@ -2447,14 +3011,46 @@ "pkg-dir/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + "prebuild-install/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=="], + "readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - "wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "run-applescript/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], - "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + "run-applescript/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "run-applescript/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "run-applescript/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + + "run-applescript/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + + "run-applescript/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + + "send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "type-is/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], "@electron/get/got/cacheable-request/normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="], + "@mongodb-js/oidc-plugin/express/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + + "@mongodb-js/oidc-plugin/express/body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + + "@mongodb-js/oidc-plugin/express/body-parser/raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="], + + "@mongodb-js/oidc-plugin/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "@mongodb-js/oidc-plugin/express/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], + + "@mongodb-js/oidc-plugin/express/send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + + "@mongodb-js/oidc-plugin/express/type-is/media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + "@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=="], @@ -2555,34 +3151,40 @@ "@stock-bot/questdb-client/eslint/file-entry-cache/flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], - "@stock-bot/web/@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=="], + "@stock-bot/web-app/@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=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/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=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/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=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - "@stock-bot/web/@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], + "@stock-bot/web-app/@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], - "@stock-bot/web/@typescript-eslint/parser/@typescript-eslint/typescript-estree/ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], + "@stock-bot/web-app/@typescript-eslint/parser/@typescript-eslint/typescript-estree/ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], - "@stock-bot/web/@typescript-eslint/parser/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + "@stock-bot/web-app/@typescript-eslint/parser/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - "@stock-bot/web/eslint/file-entry-cache/flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], + "@stock-bot/web-app/eslint/file-entry-cache/flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], "dockerode/tar-fs/tar-stream/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=="], + "mongodb-client-encryption/prebuild-install/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=="], + "mongodb-memory-server-core/mongodb/mongodb-connection-string-url/@types/whatwg-url": ["@types/whatwg-url@8.2.2", "", { "dependencies": { "@types/node": "*", "@types/webidl-conversions": "*" } }, "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA=="], "mongodb-memory-server-core/mongodb/mongodb-connection-string-url/whatwg-url": ["whatwg-url@11.0.0", "", { "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" } }, "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ=="], "pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "prebuild-install/tar-fs/tar-stream/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=="], + + "run-applescript/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "@stock-bot/config/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@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/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], @@ -2623,13 +3225,15 @@ "@stock-bot/questdb-client/@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], - "@stock-bot/web/@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "@stock-bot/web-app/@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "mongodb-client-encryption/prebuild-install/tar-fs/tar-stream/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=="], "mongodb-memory-server-core/mongodb/mongodb-connection-string-url/whatwg-url/tr46": ["tr46@3.0.0", "", { "dependencies": { "punycode": "^2.1.1" } }, "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA=="], @@ -2655,8 +3259,8 @@ "@stock-bot/questdb-client/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - "@stock-bot/web/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "@stock-bot/web-app/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], } } diff --git a/database/postgres/init/01-init-database.sql b/database/postgres/init/01-init-database.sql new file mode 100644 index 0000000..acd43b3 --- /dev/null +++ b/database/postgres/init/01-init-database.sql @@ -0,0 +1,13 @@ +-- Initialize main database and user +-- This runs first when PostgreSQL container starts + +-- Create main database if it doesn't exist +SELECT 'CREATE DATABASE trading_bot' +WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'trading_bot')\gexec + +-- Connect to the trading_bot database +\c trading_bot; + +-- Create extensions we'll need +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- For fuzzy string matching \ No newline at end of file diff --git a/database/postgres/init/01-init-schemas.sql b/database/postgres/init/01-init-schemas.sql deleted file mode 100644 index 912453b..0000000 --- a/database/postgres/init/01-init-schemas.sql +++ /dev/null @@ -1,20 +0,0 @@ --- Trading Bot Database Schema Initialization - --- Create schemas -CREATE SCHEMA IF NOT EXISTS trading; -CREATE SCHEMA IF NOT EXISTS strategy; -CREATE SCHEMA IF NOT EXISTS risk; -CREATE SCHEMA IF NOT EXISTS audit; - --- Set search path for the database -ALTER DATABASE trading_bot SET search_path TO trading, strategy, risk, audit, public; - --- Create extensions -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -CREATE EXTENSION IF NOT EXISTS "btree_gin"; -CREATE EXTENSION IF NOT EXISTS "pg_stat_statements"; - --- Create a read-only user for analytics -CREATE USER trading_reader WITH PASSWORD 'reader_pass_dev'; -GRANT CONNECT ON DATABASE trading_bot TO trading_reader; -GRANT USAGE ON SCHEMA trading, strategy, risk, audit TO trading_reader; diff --git a/database/postgres/init/02-master-schema.sql b/database/postgres/init/02-master-schema.sql new file mode 100644 index 0000000..895687e --- /dev/null +++ b/database/postgres/init/02-master-schema.sql @@ -0,0 +1,63 @@ +-- Simple Master Schema for Symbol Resolution +-- Connect to trading_bot database +\c trading_bot; + +-- Exchanges Table +CREATE TABLE IF NOT EXISTS exchanges ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + code VARCHAR(10) UNIQUE NOT NULL, -- NASDAQ, NYSE, TSX, etc. + name VARCHAR(255) NOT NULL, -- Full exchange name + country CHAR(2) NOT NULL, -- US, CA, GB + currency CHAR(3) NOT NULL DEFAULT 'USD', -- USD, CAD, GBP + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Symbols Table +CREATE TABLE IF NOT EXISTS symbols ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + symbol VARCHAR(20) NOT NULL, -- AAPL, SHOP, etc. + exchange_id UUID REFERENCES exchanges(id), + company_name VARCHAR(255), + sector VARCHAR(100), + country CHAR(2), + currency CHAR(3), + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + UNIQUE(symbol, exchange_id) +); + +-- Provider Symbol Mappings (How each provider refers to our symbols) +CREATE TABLE IF NOT EXISTS provider_mappings ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + symbol_id UUID REFERENCES symbols(id), + provider VARCHAR(50) NOT NULL, -- 'qm', 'yahoo', 'ib', etc. + provider_symbol VARCHAR(100) NOT NULL, -- How provider names it: "AAPL:NASDAQ", "SHOP.TO" + provider_exchange VARCHAR(50), -- Provider's exchange code + confidence DECIMAL(3,2) DEFAULT 1.0, -- 0.0 to 1.0 matching confidence + verified BOOLEAN DEFAULT false, -- Manually verified flag + last_seen TIMESTAMP DEFAULT NOW(), + created_at TIMESTAMP DEFAULT NOW(), + UNIQUE(provider, provider_symbol) +); + +-- Simple sync tracking table +CREATE TABLE IF NOT EXISTS sync_status ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + provider VARCHAR(50) NOT NULL, + data_type VARCHAR(50) NOT NULL, -- 'symbols', 'exchanges' + last_sync_at TIMESTAMP, + last_sync_count INTEGER DEFAULT 0, + sync_errors TEXT, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + UNIQUE(provider, data_type) +); + +-- Create basic indexes +CREATE INDEX IF NOT EXISTS idx_symbols_symbol ON symbols(symbol); +CREATE INDEX IF NOT EXISTS idx_symbols_exchange ON symbols(exchange_id); +CREATE INDEX IF NOT EXISTS idx_provider_mappings_provider ON provider_mappings(provider, provider_symbol); +CREATE INDEX IF NOT EXISTS idx_provider_mappings_symbol ON provider_mappings(symbol_id); \ No newline at end of file diff --git a/database/postgres/init/02-trading-tables.sql b/database/postgres/init/02-trading-tables.sql deleted file mode 100644 index 64409c7..0000000 --- a/database/postgres/init/02-trading-tables.sql +++ /dev/null @@ -1,93 +0,0 @@ --- Core trading tables - --- Symbols and instruments -CREATE TABLE trading.symbols ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - symbol VARCHAR(20) NOT NULL UNIQUE, - name VARCHAR(255), - exchange VARCHAR(50), - asset_type VARCHAR(20) DEFAULT 'equity', - sector VARCHAR(100), - is_active BOOLEAN DEFAULT true, - metadata JSONB DEFAULT '{}', - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Orders -CREATE TABLE trading.orders ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - symbol_id UUID REFERENCES trading.symbols(id), - strategy_id UUID, - order_type VARCHAR(20) NOT NULL, -- 'market', 'limit', 'stop', etc. - side VARCHAR(10) NOT NULL CHECK (side IN ('buy', 'sell')), - quantity DECIMAL(18,8) NOT NULL CHECK (quantity > 0), - price DECIMAL(18,8), - stop_price DECIMAL(18,8), - status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'submitted', 'filled', 'cancelled', 'rejected')), - broker_order_id VARCHAR(100), - filled_quantity DECIMAL(18,8) DEFAULT 0, - avg_fill_price DECIMAL(18,8), - commission DECIMAL(18,8) DEFAULT 0, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - CONSTRAINT valid_prices CHECK ( - (order_type = 'market') OR - (order_type = 'limit' AND price IS NOT NULL) OR - (order_type = 'stop' AND stop_price IS NOT NULL) - ) -); - --- Positions -CREATE TABLE trading.positions ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - symbol_id UUID REFERENCES trading.symbols(id), - strategy_id UUID, - quantity DECIMAL(18,8) NOT NULL, - avg_cost DECIMAL(18,8) NOT NULL, - market_value DECIMAL(18,8), - unrealized_pnl DECIMAL(18,8), - realized_pnl DECIMAL(18,8) DEFAULT 0, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - UNIQUE(symbol_id, strategy_id) -); - --- Executions/Fills -CREATE TABLE trading.executions ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - order_id UUID REFERENCES trading.orders(id), - symbol_id UUID REFERENCES trading.symbols(id), - side VARCHAR(10) NOT NULL CHECK (side IN ('buy', 'sell')), - quantity DECIMAL(18,8) NOT NULL, - price DECIMAL(18,8) NOT NULL, - commission DECIMAL(18,8) DEFAULT 0, - broker_execution_id VARCHAR(100), - executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Accounts/Portfolios -CREATE TABLE trading.accounts ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - name VARCHAR(255) NOT NULL, - account_type VARCHAR(50) DEFAULT 'paper', -- 'paper', 'live' - broker VARCHAR(50), - cash_balance DECIMAL(18,2) DEFAULT 0, - buying_power DECIMAL(18,2) DEFAULT 0, - total_value DECIMAL(18,2) DEFAULT 0, - is_active BOOLEAN DEFAULT true, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Create indexes for performance -CREATE INDEX idx_orders_symbol_created ON trading.orders(symbol_id, created_at); -CREATE INDEX idx_orders_status ON trading.orders(status); -CREATE INDEX idx_orders_strategy ON trading.orders(strategy_id); -CREATE INDEX idx_positions_strategy ON trading.positions(strategy_id); -CREATE INDEX idx_executions_order ON trading.executions(order_id); -CREATE INDEX idx_executions_symbol_time ON trading.executions(symbol_id, executed_at); - --- Grant permissions to reader -GRANT SELECT ON ALL TABLES IN SCHEMA trading TO trading_reader; diff --git a/database/postgres/init/03-initial-data.sql b/database/postgres/init/03-initial-data.sql new file mode 100644 index 0000000..c414ffc --- /dev/null +++ b/database/postgres/init/03-initial-data.sql @@ -0,0 +1,23 @@ +-- Insert initial reference data +\c trading_bot; + +-- Insert basic exchanges to start with +INSERT INTO exchanges (code, name, country, currency) VALUES + ('NASDAQ', 'NASDAQ Stock Market', 'US', 'USD'), + ('NYSE', 'New York Stock Exchange', 'US', 'USD'), + ('TSX', 'Toronto Stock Exchange', 'CA', 'CAD'), + ('LSE', 'London Stock Exchange', 'GB', 'GBP'), + ('CME', 'Chicago Mercantile Exchange', 'US', 'USD') +ON CONFLICT (code) DO NOTHING; + +-- Insert initial sync status records for QM provider +INSERT INTO sync_status (provider, data_type) VALUES + ('qm', 'symbols'), + ('qm', 'exchanges') +ON CONFLICT (provider, data_type) DO NOTHING; + +-- Show what we created +SELECT 'Database setup complete. Tables created:' as status; +SELECT table_name FROM information_schema.tables +WHERE table_schema = 'public' +ORDER BY table_name; \ No newline at end of file diff --git a/database/postgres/init/03-strategy-risk-tables.sql b/database/postgres/init/03-strategy-risk-tables.sql deleted file mode 100644 index 7824ec8..0000000 --- a/database/postgres/init/03-strategy-risk-tables.sql +++ /dev/null @@ -1,105 +0,0 @@ --- Strategy and Risk Management Tables - --- Strategies -CREATE TABLE strategy.strategies ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - name VARCHAR(255) NOT NULL, - description TEXT, - version VARCHAR(20) DEFAULT '1.0.0', - config JSONB DEFAULT '{}', - parameters JSONB DEFAULT '{}', - is_active BOOLEAN DEFAULT false, - is_enabled BOOLEAN DEFAULT true, - created_by VARCHAR(255), - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Strategy executions/runs -CREATE TABLE strategy.executions ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - strategy_id UUID REFERENCES strategy.strategies(id), - status VARCHAR(20) DEFAULT 'running' CHECK (status IN ('running', 'stopped', 'error', 'completed')), - started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - stopped_at TIMESTAMP WITH TIME ZONE, - error_message TEXT, - execution_stats JSONB DEFAULT '{}', - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Strategy signals -CREATE TABLE strategy.signals ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - strategy_id UUID REFERENCES strategy.strategies(id), - symbol_id UUID REFERENCES trading.symbols(id), - signal_type VARCHAR(20) NOT NULL CHECK (signal_type IN ('buy', 'sell', 'hold')), - strength DECIMAL(3,2) CHECK (strength >= 0 AND strength <= 1), -- 0.0 to 1.0 - confidence DECIMAL(3,2) CHECK (confidence >= 0 AND confidence <= 1), - target_price DECIMAL(18,8), - stop_loss DECIMAL(18,8), - take_profit DECIMAL(18,8), - metadata JSONB DEFAULT '{}', - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Risk limits -CREATE TABLE risk.limits ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - strategy_id UUID REFERENCES strategy.strategies(id), - account_id UUID REFERENCES trading.accounts(id), - limit_type VARCHAR(50) NOT NULL, -- 'max_position_size', 'max_daily_loss', 'max_drawdown', etc. - limit_value DECIMAL(18,8) NOT NULL, - current_value DECIMAL(18,8) DEFAULT 0, - threshold_warning DECIMAL(18,8), -- Warning at X% of limit - is_active BOOLEAN DEFAULT true, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Risk events/alerts -CREATE TABLE risk.events ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - limit_id UUID REFERENCES risk.limits(id), - strategy_id UUID, - event_type VARCHAR(50) NOT NULL, -- 'warning', 'breach', 'resolved' - severity VARCHAR(20) DEFAULT 'medium' CHECK (severity IN ('low', 'medium', 'high', 'critical')), - message TEXT NOT NULL, - current_value DECIMAL(18,8), - limit_value DECIMAL(18,8), - metadata JSONB DEFAULT '{}', - acknowledged BOOLEAN DEFAULT false, - acknowledged_by VARCHAR(255), - acknowledged_at TIMESTAMP WITH TIME ZONE, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Performance tracking -CREATE TABLE strategy.performance ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - strategy_id UUID REFERENCES strategy.strategies(id), - date DATE NOT NULL, - total_return DECIMAL(10,4), - daily_return DECIMAL(10,4), - sharpe_ratio DECIMAL(10,4), - max_drawdown DECIMAL(10,4), - win_rate DECIMAL(5,4), - profit_factor DECIMAL(10,4), - total_trades INTEGER DEFAULT 0, - winning_trades INTEGER DEFAULT 0, - losing_trades INTEGER DEFAULT 0, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - UNIQUE(strategy_id, date) -); - --- Create indexes -CREATE INDEX idx_strategies_active ON strategy.strategies(is_active, is_enabled); -CREATE INDEX idx_executions_strategy ON strategy.executions(strategy_id); -CREATE INDEX idx_signals_strategy_time ON strategy.signals(strategy_id, created_at); -CREATE INDEX idx_signals_symbol ON strategy.signals(symbol_id); -CREATE INDEX idx_limits_strategy ON risk.limits(strategy_id); -CREATE INDEX idx_risk_events_severity ON risk.events(severity, created_at); -CREATE INDEX idx_performance_strategy_date ON strategy.performance(strategy_id, date); - --- Grant permissions -GRANT SELECT ON ALL TABLES IN SCHEMA strategy TO trading_reader; -GRANT SELECT ON ALL TABLES IN SCHEMA risk TO trading_reader; diff --git a/database/postgres/init/04-audit-tables.sql b/database/postgres/init/04-audit-tables.sql deleted file mode 100644 index 1371520..0000000 --- a/database/postgres/init/04-audit-tables.sql +++ /dev/null @@ -1,59 +0,0 @@ --- Audit and System Tables - --- System events audit -CREATE TABLE audit.system_events ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - service_name VARCHAR(100) NOT NULL, - event_type VARCHAR(50) NOT NULL, - event_data JSONB DEFAULT '{}', - user_id VARCHAR(255), - ip_address INET, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Trading events audit -CREATE TABLE audit.trading_events ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - event_type VARCHAR(50) NOT NULL, -- 'order_created', 'order_filled', 'position_opened', etc. - entity_type VARCHAR(50) NOT NULL, -- 'order', 'position', 'execution' - entity_id UUID NOT NULL, - old_values JSONB, - new_values JSONB, - changed_by VARCHAR(255), - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Service health monitoring -CREATE TABLE audit.service_health ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - service_name VARCHAR(100) NOT NULL, - status VARCHAR(20) NOT NULL CHECK (status IN ('healthy', 'unhealthy', 'degraded')), - version VARCHAR(50), - uptime_seconds INTEGER, - memory_usage_mb INTEGER, - cpu_usage_percent DECIMAL(5,2), - last_health_check TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - metadata JSONB DEFAULT '{}', - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Configuration changes -CREATE TABLE audit.config_changes ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - config_key VARCHAR(255) NOT NULL, - old_value TEXT, - new_value TEXT, - changed_by VARCHAR(255), - reason TEXT, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Create indexes -CREATE INDEX idx_system_events_service_time ON audit.system_events(service_name, created_at); -CREATE INDEX idx_trading_events_type_time ON audit.trading_events(event_type, created_at); -CREATE INDEX idx_trading_events_entity ON audit.trading_events(entity_type, entity_id); -CREATE INDEX idx_service_health_name_time ON audit.service_health(service_name, created_at); -CREATE INDEX idx_config_changes_key_time ON audit.config_changes(config_key, created_at); - --- Grant permissions -GRANT SELECT ON ALL TABLES IN SCHEMA audit TO trading_reader; diff --git a/database/postgres/init/04-provider-exchange-mapping-schema.sql b/database/postgres/init/04-provider-exchange-mapping-schema.sql new file mode 100644 index 0000000..bba44a7 --- /dev/null +++ b/database/postgres/init/04-provider-exchange-mapping-schema.sql @@ -0,0 +1,73 @@ +-- Enhanced Schema with Provider Exchange Mappings +-- Connect to trading_bot database +\c trading_bot; + +-- First, rename is_active to active in existing tables +ALTER TABLE IF EXISTS exchanges RENAME COLUMN is_active TO active; +ALTER TABLE IF EXISTS symbols RENAME COLUMN is_active TO active; + +-- Provider Exchange Mappings Table +-- Maps provider-specific exchange codes to our master exchanges +CREATE TABLE IF NOT EXISTS provider_exchange_mappings ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + provider VARCHAR(50) NOT NULL, -- 'qm', 'eod', 'ib', etc. + provider_exchange_code VARCHAR(50) NOT NULL, -- Provider's exchange code: 'NYE', 'US', 'NASDAQ' + provider_exchange_name VARCHAR(255), -- Provider's exchange name + master_exchange_id UUID REFERENCES exchanges(id), + country_code CHAR(2), -- Provider's country code + currency CHAR(3), -- Provider's currency + confidence DECIMAL(3,2) DEFAULT 0.8, -- Mapping confidence (0.0 to 1.0) + active BOOLEAN DEFAULT false, -- Manual activation flag + verified BOOLEAN DEFAULT false, -- Manually verified flag + auto_mapped BOOLEAN DEFAULT true, -- Was this auto-created by sync? + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + UNIQUE(provider, provider_exchange_code) +); + +-- Create indexes for provider exchange mappings +CREATE INDEX IF NOT EXISTS idx_provider_exchange_mappings_provider ON provider_exchange_mappings(provider); +CREATE INDEX IF NOT EXISTS idx_provider_exchange_mappings_master ON provider_exchange_mappings(master_exchange_id); +CREATE INDEX IF NOT EXISTS idx_provider_exchange_mappings_active ON provider_exchange_mappings(provider, active); + +-- Update existing exchanges to be inactive by default (for new syncs) +-- But preserve any existing active status +-- This only affects future INSERTs, not existing data + +-- Add some useful views for management +CREATE OR REPLACE VIEW exchange_provider_summary AS +SELECT + e.code as master_code, + e.name as master_name, + e.country, + e.currency, + e.active as master_active, + COUNT(pem.id) as provider_mappings, + COUNT(CASE WHEN pem.active = true THEN 1 END) as active_mappings, + COUNT(CASE WHEN pem.verified = true THEN 1 END) as verified_mappings, + STRING_AGG(DISTINCT pem.provider, ', ') as providers +FROM exchanges e +LEFT JOIN provider_exchange_mappings pem ON e.id = pem.master_exchange_id +GROUP BY e.id, e.code, e.name, e.country, e.currency, e.active +ORDER BY e.code; + +CREATE OR REPLACE VIEW provider_exchange_details AS +SELECT + pem.provider, + pem.provider_exchange_code, + pem.provider_exchange_name, + pem.country_code, + pem.currency, + pem.active, + pem.verified, + pem.auto_mapped, + pem.confidence, + e.code as master_exchange_code, + e.name as master_exchange_name, + e.active as master_active +FROM provider_exchange_mappings pem +JOIN exchanges e ON pem.master_exchange_id = e.id +ORDER BY pem.provider, pem.provider_exchange_code; + +-- Show what we created +SELECT 'Enhanced provider exchange mapping schema created' as status; \ No newline at end of file diff --git a/database/postgres/init/05-initial-data.sql b/database/postgres/init/05-initial-data.sql deleted file mode 100644 index b2e342a..0000000 --- a/database/postgres/init/05-initial-data.sql +++ /dev/null @@ -1,55 +0,0 @@ --- Insert initial reference data - --- Insert common symbols -INSERT INTO trading.symbols (symbol, name, exchange, asset_type, sector) VALUES -('AAPL', 'Apple Inc.', 'NASDAQ', 'equity', 'Technology'), -('GOOGL', 'Alphabet Inc.', 'NASDAQ', 'equity', 'Technology'), -('MSFT', 'Microsoft Corporation', 'NASDAQ', 'equity', 'Technology'), -('AMZN', 'Amazon.com Inc.', 'NASDAQ', 'equity', 'Consumer Discretionary'), -('TSLA', 'Tesla Inc.', 'NASDAQ', 'equity', 'Consumer Discretionary'), -('NVDA', 'NVIDIA Corporation', 'NASDAQ', 'equity', 'Technology'), -('META', 'Meta Platforms Inc.', 'NASDAQ', 'equity', 'Technology'), -('NFLX', 'Netflix Inc.', 'NASDAQ', 'equity', 'Communication Services'), -('SPY', 'SPDR S&P 500 ETF Trust', 'NYSE', 'etf', 'Broad Market'), -('QQQ', 'Invesco QQQ Trust', 'NASDAQ', 'etf', 'Technology'), -('BTC-USD', 'Bitcoin USD', 'CRYPTO', 'cryptocurrency', 'Digital Assets'), -('ETH-USD', 'Ethereum USD', 'CRYPTO', 'cryptocurrency', 'Digital Assets'); - --- Insert default trading account -INSERT INTO trading.accounts (name, account_type, broker, cash_balance, buying_power, total_value) VALUES -('Demo Account', 'paper', 'demo', 100000.00, 100000.00, 100000.00); - --- Insert demo strategy -INSERT INTO strategy.strategies (name, description, config, parameters, is_active) VALUES -('Demo Mean Reversion', 'Simple mean reversion strategy for demonstration', - '{"timeframe": "1h", "lookback_period": 20}', - '{"rsi_oversold": 30, "rsi_overbought": 70, "position_size": 0.1}', - false); - --- Insert basic risk limits -INSERT INTO risk.limits (strategy_id, limit_type, limit_value, threshold_warning) -SELECT s.id, 'max_position_size', 10000.00, 8000.00 -FROM strategy.strategies s -WHERE s.name = 'Demo Mean Reversion'; - -INSERT INTO risk.limits (strategy_id, limit_type, limit_value, threshold_warning) -SELECT s.id, 'max_daily_loss', 5000.00, 4000.00 -FROM strategy.strategies s -WHERE s.name = 'Demo Mean Reversion'; - --- Create updated_at trigger function -CREATE OR REPLACE FUNCTION update_updated_at_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = NOW(); - RETURN NEW; -END; -$$ language 'plpgsql'; - --- Apply updated_at triggers -CREATE TRIGGER update_symbols_updated_at BEFORE UPDATE ON trading.symbols FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_orders_updated_at BEFORE UPDATE ON trading.orders FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_positions_updated_at BEFORE UPDATE ON trading.positions FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_accounts_updated_at BEFORE UPDATE ON trading.accounts FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_strategies_updated_at BEFORE UPDATE ON strategy.strategies FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -CREATE TRIGGER update_limits_updated_at BEFORE UPDATE ON risk.limits FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); diff --git a/database/postgres/providers/01-ib-simple.sql b/database/postgres/providers/01-ib-simple.sql deleted file mode 100644 index e69de29..0000000 diff --git a/database/postgres/providers/01-ib.sql b/database/postgres/providers/01-ib.sql deleted file mode 100644 index 6f6bc4d..0000000 --- a/database/postgres/providers/01-ib.sql +++ /dev/null @@ -1,51 +0,0 @@ --- ============================================================================= --- Interactive Brokers Simple Schema Setup --- ============================================================================= - --- Create dedicated schema for IB data -CREATE SCHEMA IF NOT EXISTS ib_data; - --- ============================================================================= --- Simple Exchanges Table --- ============================================================================= - -CREATE TABLE IF NOT EXISTS ib_data.exchanges ( - id SERIAL PRIMARY KEY, - exchange_code VARCHAR(20) NOT NULL UNIQUE, - exchange_name TEXT NOT NULL, - country VARCHAR(100), - region VARCHAR(50), - country_code VARCHAR(3), - assets TEXT, - is_active BOOLEAN DEFAULT true, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Create indexes for performance -CREATE INDEX IF NOT EXISTS idx_exchanges_code ON ib_data.exchanges(exchange_code); -CREATE INDEX IF NOT EXISTS idx_exchanges_country ON ib_data.exchanges(country_code); -CREATE INDEX IF NOT EXISTS idx_exchanges_region ON ib_data.exchanges(region); -CREATE INDEX IF NOT EXISTS idx_exchanges_active ON ib_data.exchanges(is_active); - --- ============================================================================= --- Permissions --- ============================================================================= - --- Grant usage on schema -GRANT USAGE ON SCHEMA ib_data TO PUBLIC; - --- Grant permissions on tables -GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA ib_data TO PUBLIC; -GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA ib_data TO PUBLIC; - --- Set default permissions for future tables -ALTER DEFAULT PRIVILEGES IN SCHEMA ib_data GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO PUBLIC; -ALTER DEFAULT PRIVILEGES IN SCHEMA ib_data GRANT USAGE, SELECT ON SEQUENCES TO PUBLIC; - --- ============================================================================= --- Comments --- ============================================================================= - -COMMENT ON SCHEMA ib_data IS 'Interactive Brokers market data schema (simplified)'; -COMMENT ON TABLE ib_data.exchanges IS 'Trading exchanges from Interactive Brokers'; diff --git a/database/postgres/providers/README.md b/database/postgres/providers/README.md deleted file mode 100644 index a8170e5..0000000 --- a/database/postgres/providers/README.md +++ /dev/null @@ -1,104 +0,0 @@ -# Interactive Brokers Database Setup - -This directory contains the PostgreSQL schema setup for Interactive Brokers data. - -## Quick Setup - -### 1. **Create the Schema and Tables** -```bash -# Run the SQL schema setup -bun run db:setup - -# Or manually with psql: -psql -U postgres -d stock_bot -f database/postgres/providers/01-ib.sql -``` - -### 2. **Populate with Exchange Data** -```bash -# Populate exchanges from ib-exchanges.json -bun run db:populate-ib - -# Or run the complete setup (schema + data): -bun run db:setup-ib -``` - -## What Gets Created - -### 📊 **Schema: `ib_data`** -- `exchanges` - All IB trading exchanges with metadata -- `asset_types` - Types of financial instruments (Stocks, Options, etc.) -- `exchange_assets` - Many-to-many mapping of exchanges to asset types -- `securities` - Individual tradeable instruments -- `market_data` - Real-time and historical price data -- `data_fetch_jobs` - Queue for data collection tasks - -### 🔍 **Views** -- `exchanges_with_assets` - Exchanges with their supported asset types -- `latest_market_data` - Most recent market data per security -- `securities_full_view` - Securities with full exchange and asset type info - -### ⚡ **Functions** -- `get_or_create_exchange()` - Utility to insert/update exchanges -- `add_assets_to_exchange()` - Parse and add asset types to exchanges - -## Database Structure - -```sql --- Example queries you can run after setup: - --- View all exchanges with their supported assets -SELECT * FROM ib_data.exchanges_with_assets LIMIT 10; - --- Count exchanges by region -SELECT region, COUNT(*) -FROM ib_data.exchanges -GROUP BY region -ORDER BY COUNT(*) DESC; - --- Find exchanges that support stocks -SELECT e.exchange_code, e.exchange_name, e.country -FROM ib_data.exchanges e -JOIN ib_data.exchange_assets ea ON e.id = ea.exchange_id -JOIN ib_data.asset_types at ON ea.asset_type_id = at.id -WHERE at.code = 'Stocks' -ORDER BY e.exchange_code; -``` - -## Environment Variables - -Set these in your `.env` file for the populate script: - -```bash -DB_HOST=localhost -DB_PORT=5432 -DB_NAME=stock_bot -DB_USER=postgres -DB_PASSWORD=your_password -``` - -## Integration with Your Code - -The schema is designed to work with your existing `ib.tasks.ts` file: - -```typescript -// Your fetchSession() function can now store data like: -import { query } from '@stock-bot/postgres-client'; - -// Create a fetch job -await query(` - INSERT INTO ib_data.data_fetch_jobs (job_type, status, metadata) - VALUES ('SYMBOL_SUMMARY', 'PENDING', $1) -`, [{ url: 'https://...', proxy: '...' }]); - -// Store exchange data -const exchangeId = await query(` - SELECT ib_data.get_or_create_exchange($1, $2, $3, $4, $5) -`, ['NASDAQ', 'NASDAQ Global Select Market', 'United States', 'Americas', 'US']); -``` - -## Next Steps - -1. ✅ Run the setup scripts -2. 🔧 Update your IB tasks to use the database -3. 📊 Start collecting market data -4. 🚀 Build your trading strategies on top of this data layer! diff --git a/database/postgres/scripts/README.md b/database/postgres/scripts/README.md deleted file mode 100644 index dac5fe9..0000000 --- a/database/postgres/scripts/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Database Scripts - -**Simplified database initialization system for Interactive Brokers data.** - -## Quick Start - -```bash -# Initialize everything (recommended) -bun run db:init - -# Or run Interactive Brokers setup directly: -bun run db:setup-ib # Create schema and populate IB data -``` - -## What We Built - -✅ **Simplified from complex multi-table schema to exchanges-only** -✅ **Single script setup** - `setup-ib.ts` handles both schema and data -✅ **Structured logging** with `@stock-bot/logger` -✅ **184 exchanges populated** from JSON data -✅ **Proper error handling** with helpful troubleshooting messages - -## Scripts - -### `setup-ib.ts` - Interactive Brokers Complete Setup -**Main script for IB setup** - Sets up schema and populates exchange data in one go. - -### `init.ts` -Main initialization script that orchestrates setup for all providers. - -## Database Schema - -### IB Data (`ib_data` schema) -- `exchanges` - Trading exchanges with metadata -- `upsert_exchange()` - Function to insert/update exchanges - -## Package.json Commands - -```json -{ - "db:init": "Run complete database initialization", - "db:setup-ib": "Complete IB setup (schema + data)" -} -``` - -## Adding New Providers - -1. Create `{provider}.sql` in `database/postgres/providers/` -2. Create `{provider}.ts` script -3. Add to `init.ts` and `package.json` - -## Requirements - -- PostgreSQL running -- Database configured in `.env` -- `ib-exchanges.json` file in `apps/data-service/src/setup/` diff --git a/database/postgres/scripts/ib.ts b/database/postgres/scripts/ib.ts deleted file mode 100644 index e69de29..0000000 diff --git a/database/postgres/scripts/init.ts b/database/postgres/scripts/init.ts deleted file mode 100644 index 33a7def..0000000 --- a/database/postgres/scripts/init.ts +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bun -/** - * Main database initialization script - * Sets up the database schema and populates with initial data - */ - -import { getLogger } from '@stock-bot/logger'; -import { setupIB } from './setup-ib'; - -const logger = getLogger('db-init'); - -async function main() { - logger.info('Starting database initialization'); - - try { - // Step 1: Setup Interactive Brokers (schema + data) - logger.info('Setting up Interactive Brokers (schema + data)'); - await setupIB(); - logger.info('IB setup completed'); - - // Future providers can be added here: - // await setupAlpaca(); - // await setupPolygon(); - - logger.info('Database initialization completed successfully'); - - } catch (error) { - logger.error('Database initialization failed', { error }); - process.exit(1); - } -} - -// Run the script -if (import.meta.main) { - main().catch((error) => { - console.error('Init script failed:', error); - process.exit(1); - }); -} - -export { main as initDatabase }; diff --git a/database/postgres/scripts/populate-ib-exchanges-simple.ts b/database/postgres/scripts/populate-ib-exchanges-simple.ts deleted file mode 100644 index e69de29..0000000 diff --git a/database/postgres/scripts/populate-ib-exchanges.ts b/database/postgres/scripts/populate-ib-exchanges.ts deleted file mode 100644 index e69de29..0000000 diff --git a/database/postgres/scripts/setup-ib-fast.ts b/database/postgres/scripts/setup-ib-fast.ts deleted file mode 100644 index e69de29..0000000 diff --git a/database/postgres/scripts/setup-ib-schema-simple.ts b/database/postgres/scripts/setup-ib-schema-simple.ts deleted file mode 100644 index e69de29..0000000 diff --git a/database/postgres/scripts/setup-ib-schema.ts b/database/postgres/scripts/setup-ib-schema.ts deleted file mode 100644 index e69de29..0000000 diff --git a/database/postgres/scripts/setup-ib.ts b/database/postgres/scripts/setup-ib.ts deleted file mode 100644 index a9f434d..0000000 --- a/database/postgres/scripts/setup-ib.ts +++ /dev/null @@ -1,366 +0,0 @@ -#!/usr/bin/env bun -/** - * Interactive Brokers complete setup script - * Sets up schema and populates IB exchanges from ib-exchanges.json into PostgreSQL - */ - -import { postgresConfig } from '@stock-bot/config'; -import { getLogger } from '@stock-bot/logger'; -import { PostgreSQLClient } from '@stock-bot/postgres-client'; -import { readFileSync } from 'fs'; -import { join } from 'path'; - -// Initialize logger -const logger = getLogger('ib-setup'); - -// Type definitions based on the JSON structure -interface IBExchange { - id: string; - name: string; - country: string; - region: string; - assets: string; - country_code: string; -} - -async function connectToDatabase(): Promise { - logger.info('Connecting to PostgreSQL', { - host: postgresConfig.POSTGRES_HOST, - port: postgresConfig.POSTGRES_PORT, - database: postgresConfig.POSTGRES_DATABASE - }); - - try { - const client = new PostgreSQLClient(); - await client.connect(); - - logger.info('Connected to PostgreSQL database'); - - // Test the connection - const result = await client.query('SELECT version()'); - const version = result.rows[0].version.split(' ')[0]; - logger.info('PostgreSQL connection verified', { version }); - - return client; - } catch (error) { - logger.error('Failed to connect to PostgreSQL', { error }); - throw error; - } -} - -async function runSchemaSetup(client: PostgreSQLClient) { - try { - logger.info('Loading schema SQL file'); - - const schemaPath = join(process.cwd(), 'database/postgres/providers/01-ib.sql'); - const schemaSql = readFileSync(schemaPath, 'utf-8'); - - logger.info('Executing schema setup'); - - // Execute the entire SQL file as one statement to handle multi-line functions - try { - await client.query(schemaSql); - logger.info('Schema setup completed successfully'); - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); - // Check if it's just "already exists" errors - if (errorMessage.includes('already exists')) { - logger.info('Schema setup completed (some objects already existed)'); - } else { - logger.error('Error executing schema setup', { error: errorMessage }); - throw error; - } - } - - // Verify the setup - await verifySchemaSetup(client); - - } catch (error) { - logger.error('Schema setup failed', { error }); - throw error; - } -} - -async function verifySchemaSetup(client: PostgreSQLClient) { - logger.info('Verifying schema setup'); - - try { - // Check if schema exists - const schemaCheck = await client.query(` - SELECT schema_name - FROM information_schema.schemata - WHERE schema_name = 'ib_data' - `); - - if (schemaCheck.rows.length === 0) { - throw new Error('ib_data schema was not created'); - } - - // Check tables - const tableCheck = await client.query(` - SELECT table_name - FROM information_schema.tables - WHERE table_schema = 'ib_data' - ORDER BY table_name - `); - - const actualTables = tableCheck.rows.map((row: any) => row.table_name); - - // Check functions - const functionCheck = await client.query(` - SELECT routine_name - FROM information_schema.routines - WHERE routine_schema = 'ib_data' - ORDER BY routine_name - `); - - const functions = functionCheck.rows.map((row: any) => row.routine_name); - - logger.info('Schema verification completed', { - schema: 'ib_data', - tables: actualTables, - functions: functions - }); - - } catch (error) { - logger.error('Schema verification failed', { error }); - throw error; - } -} - -async function loadExchangesData(): Promise { - try { - // Look for the JSON file in the project root - const jsonPath = join(process.cwd(), 'apps/data-service/src/setup/ib-exchanges.json'); - - logger.info('Loading exchanges from file', { path: jsonPath }); - const jsonData = readFileSync(jsonPath, 'utf-8'); - - // Remove comment lines if they exist - const cleanJsonData = jsonData.replace(/^\/\/.*$/gm, ''); - const exchanges: IBExchange[] = JSON.parse(cleanJsonData); - - // Filter out incomplete entries and deduplicate by exchange code - const validExchanges = exchanges.filter(exchange => - exchange.id && - exchange.name && - exchange.country_code && - exchange.id.trim() !== '' && - exchange.name.trim() !== '' && - exchange.country_code.trim() !== '' - ); - - // Deduplicate by exchange code (keep the first occurrence) - const exchangeMap = new Map(); - validExchanges.forEach(exchange => { - if (!exchangeMap.has(exchange.id)) { - exchangeMap.set(exchange.id, exchange); - } - }); - const uniqueExchanges = Array.from(exchangeMap.values()); - - logger.info('Exchanges loaded successfully', { - totalExchanges: exchanges.length, - validExchanges: validExchanges.length, - uniqueExchanges: uniqueExchanges.length, - duplicatesRemoved: validExchanges.length - uniqueExchanges.length, - filteredOut: exchanges.length - validExchanges.length - }); - - if (validExchanges.length !== exchanges.length) { - logger.warn('Some exchanges were filtered out due to incomplete data', { - filteredCount: exchanges.length - validExchanges.length - }); - } - - if (uniqueExchanges.length !== validExchanges.length) { - logger.warn('Duplicate exchange codes found and removed', { - duplicateCount: validExchanges.length - uniqueExchanges.length - }); - } - - return uniqueExchanges; - } catch (error) { - logger.error('Error loading exchanges JSON', { error }); - throw error; - } -} - -async function populateExchanges(client: PostgreSQLClient, exchanges: IBExchange[]): Promise { - logger.info('Starting batch exchange population', { - totalExchanges: exchanges.length - }); - - try { - // Use the new batchUpsert method for fast population - const result = await client.batchUpsert( - 'ib_data.exchanges', - exchanges.map(ex => ({ - exchange_code: ex.id, - exchange_name: ex.name, - country: ex.country || null, - region: ex.region || null, - country_code: ex.country_code, - assets: ex.assets || null - })), - 'exchange_code', - { chunkSize: 100 } - ); - - logger.info('Batch exchange population completed', { - insertedCount: result.insertedCount, - updatedCount: result.updatedCount, - totalProcessed: result.insertedCount + result.updatedCount - }); - - } catch (error) { - logger.error('Batch exchange population failed', { error }); - throw error; - } -} - -async function verifyData(client: PostgreSQLClient) { - logger.info('Verifying populated data'); - - try { - // Count exchanges - const exchangeCount = await client.query(` - SELECT COUNT(*) as count FROM ib_data.exchanges - `); - - // Get exchanges by region - const regionStats = await client.query(` - SELECT region, COUNT(*) as count - FROM ib_data.exchanges - WHERE region IS NOT NULL - GROUP BY region - ORDER BY count DESC - `); - - // Get sample exchanges - const sampleExchanges = await client.query(` - SELECT - exchange_code, - exchange_name, - country, - region, - country_code, - assets - FROM ib_data.exchanges - ORDER BY exchange_code - LIMIT 10 - `); - - const totalExchanges = exchangeCount.rows[0].count; - logger.info('Data verification completed', { totalExchanges }); - - if (regionStats.rows.length > 0) { - logger.info('Exchanges by region', { - regions: regionStats.rows.map((row: any) => ({ - region: row.region, - count: row.count - })) - }); - } - - logger.info('Sample exchanges', { - samples: sampleExchanges.rows.slice(0, 5).map((row: any) => ({ - code: row.exchange_code, - name: row.exchange_name, - country: row.country, - region: row.region, - assets: row.assets - })) - }); - - } catch (error) { - logger.error('Data verification failed', { error }); - throw error; - } -} - -async function main() { - logger.info('Starting Interactive Brokers complete setup (schema + data)'); - logger.info('Database configuration', { - database: postgresConfig.POSTGRES_DATABASE, - host: postgresConfig.POSTGRES_HOST, - port: postgresConfig.POSTGRES_PORT, - user: postgresConfig.POSTGRES_USERNAME, - ssl: postgresConfig.POSTGRES_SSL - }); - - let client: PostgreSQLClient | null = null; - - try { - // Connect to database - client = await connectToDatabase(); - - // Step 1: Setup schema - logger.info('Step 1: Setting up database schema'); - await runSchemaSetup(client); - - // Step 2: Load exchange data - logger.info('Step 2: Loading exchange data'); - const exchanges = await loadExchangesData(); - - if (exchanges.length === 0) { - logger.warn('No valid exchanges found to process'); - return; - } - - // Step 3: Populate exchanges with batch upsert - logger.info('Step 3: Populating exchanges (batch mode)'); - await populateExchanges(client, exchanges); - - // Step 4: Verify the data - logger.info('Step 4: Verifying setup and data'); - await verifyData(client); - - logger.info('Interactive Brokers setup completed successfully'); - logger.info('Next steps', { - suggestions: [ - 'Start your data service', - 'Begin collecting market data', - 'Connect to Interactive Brokers API' - ] - }); - - } catch (error: unknown) { - logger.error('IB setup failed', { error }); - - // Provide helpful error messages - if (error && typeof error === 'object' && 'code' in error && error.code === 'ECONNREFUSED') { - logger.error('Database connection refused', { - troubleshooting: [ - 'Make sure PostgreSQL is running', - 'Check your database configuration in .env file', - 'Verify the database connection details' - ] - }); - } else if (error && typeof error === 'object' && 'message' in error && - typeof error.message === 'string' && - error.message.includes('database') && - error.message.includes('does not exist')) { - logger.error('Database does not exist', { - suggestion: `Create database first: createdb ${postgresConfig.POSTGRES_DATABASE}` - }); - } - - process.exit(1); - } finally { - if (client) { - await client.disconnect(); - logger.info('Database connection closed'); - } - } -} - -// Run the script -if (import.meta.main) { - main().catch((error) => { - console.error('IB setup script failed:', error); - process.exit(1); - }); -} - -export { main as setupIB }; diff --git a/database/postgres/scripts/setup.ts b/database/postgres/scripts/setup.ts deleted file mode 100644 index e69de29..0000000 diff --git a/libs/http/src/adapters/axios-adapter.ts b/libs/http/src/adapters/axios-adapter.ts index cb98a5c..42eb73a 100644 --- a/libs/http/src/adapters/axios-adapter.ts +++ b/libs/http/src/adapters/axios-adapter.ts @@ -11,9 +11,9 @@ export class AxiosAdapter implements RequestAdapter { canHandle(config: RequestConfig): boolean { // Axios handles SOCKS proxies return Boolean( - config.proxy && - typeof config.proxy !== 'string' && - (config.proxy.protocol === 'socks4' || config.proxy.protocol === 'socks5') + config.proxy && + typeof config.proxy !== 'string' && + (config.proxy.protocol === 'socks4' || config.proxy.protocol === 'socks5') ); } diff --git a/libs/postgres-client/src/index.ts b/libs/postgres-client/src/index.ts index d646d6d..e1ab767 100644 --- a/libs/postgres-client/src/index.ts +++ b/libs/postgres-client/src/index.ts @@ -31,4 +31,9 @@ export type { } from './types'; // Utils -export { createPostgreSQLClient, getPostgreSQLClient } from './factory'; +export { + createPostgreSQLClient, + getPostgreSQLClient, + connectPostgreSQL, + disconnectPostgreSQL, +} from './factory'; diff --git a/package.json b/package.json index a4960a1..5e3aa96 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "devDependencies": { "@eslint/js": "^9.28.0", "@ianvs/prettier-plugin-sort-imports": "^4.4.2", + "@modelcontextprotocol/server-postgres": "^0.6.2", "@testcontainers/mongodb": "^10.7.2", "@testcontainers/postgresql": "^10.7.2", "@types/bun": "latest", @@ -74,6 +75,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^7.2.1", + "mongodb-mcp-server": "^0.1.1", "mongodb-memory-server": "^9.1.6", "pg-mem": "^2.8.1", "prettier": "^3.5.3", diff --git a/scripts/setup-mcp.sh b/scripts/setup-mcp.sh new file mode 100755 index 0000000..25fefb6 --- /dev/null +++ b/scripts/setup-mcp.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# Setup MCP Servers for Stock Bot +# This script helps set up Model Context Protocol servers for PostgreSQL and MongoDB + +set -e + +echo "🚀 Setting up MCP servers for Stock Bot..." + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Check if infrastructure is running +echo -e "\n${YELLOW}📊 Checking infrastructure status...${NC}" + +# Check PostgreSQL +if nc -z localhost 5432; then + echo -e "${GREEN}✅ PostgreSQL is running on port 5432${NC}" + PG_RUNNING=true +else + echo -e "${RED}❌ PostgreSQL is not running on port 5432${NC}" + PG_RUNNING=false +fi + +# Check MongoDB +if nc -z localhost 27017; then + echo -e "${GREEN}✅ MongoDB is running on port 27017${NC}" + MONGO_RUNNING=true +else + echo -e "${RED}❌ MongoDB is not running on port 27017${NC}" + MONGO_RUNNING=false +fi + +# Start infrastructure if needed +if [ "$PG_RUNNING" = false ] || [ "$MONGO_RUNNING" = false ]; then + echo -e "\n${YELLOW}🔧 Starting required infrastructure...${NC}" + bun run infra:up + echo -e "${GREEN}✅ Infrastructure started${NC}" + + # Wait a moment for services to be ready + echo -e "${YELLOW}⏳ Waiting for services to be ready...${NC}" + sleep 5 +fi + +echo -e "\n${YELLOW}🔧 Testing MCP server connections...${NC}" + +# Get project paths +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# Test PostgreSQL MCP server +echo -e "\n${YELLOW}Testing PostgreSQL MCP server...${NC}" +if npm list @modelcontextprotocol/server-postgres --prefix "$PROJECT_ROOT" >/dev/null 2>&1; then + echo -e "${GREEN}✅ PostgreSQL MCP server package is installed${NC}" + echo -e "${YELLOW} Package: @modelcontextprotocol/server-postgres v0.6.2${NC}" +else + echo -e "${RED}❌ PostgreSQL MCP server package not found${NC}" +fi + +# Test MongoDB MCP server +echo -e "\n${YELLOW}Testing MongoDB MCP server...${NC}" +if npm list mongodb-mcp-server --prefix "$PROJECT_ROOT" >/dev/null 2>&1; then + echo -e "${GREEN}✅ MongoDB MCP server package is installed${NC}" + echo -e "${YELLOW} Package: mongodb-mcp-server v0.1.1 (official MongoDB team)${NC}" +else + echo -e "${RED}❌ MongoDB MCP server package not found${NC}" +fi + +echo -e "\n${GREEN}🎉 MCP setup complete!${NC}" +echo -e "\n${YELLOW}📋 Configuration saved to: .vscode/mcp.json${NC}" +echo -e "\n${YELLOW}🔗 Connection details:${NC}" +echo -e " PostgreSQL: postgresql://trading_user:trading_pass_dev@localhost:5432/trading_bot" +echo -e " MongoDB: mongodb://trading_admin:trading_mongo_dev@localhost:27017/stock?authSource=admin" + +echo -e "\n${YELLOW}📖 Usage:${NC}" +echo -e " - The MCP servers are configured in .vscode/mcp.json" +echo -e " - Claude Code will automatically use these servers when they're available" +echo -e " - Make sure your infrastructure is running with: bun run infra:up" + +echo -e "\n${GREEN}✨ Ready to use MCP with PostgreSQL and MongoDB!${NC}" \ No newline at end of file