From 269364fbc890a8797ac459f54e276e161a8d1e4d Mon Sep 17 00:00:00 2001 From: Boki Date: Wed, 18 Jun 2025 21:03:45 -0400 Subject: [PATCH] switched to new config and removed old --- apps/data-service/package.json | 4 +- apps/data-service/src/index.ts | 32 +- apps/data-service/tsconfig.json | 4 +- apps/data-service/turbo.json | 4 +- apps/data-sync-service/package.json | 2 +- apps/data-sync-service/src/index.ts | 8 +- apps/data-sync-service/tsconfig.json | 38 +- apps/web-api/package.json | 2 +- apps/web-api/src/index.ts | 6 +- bun.lock | 119 +---- config/default.json | 16 + libs/config-new/.env.example | 50 -- libs/config-new/README.md | 243 ---------- libs/config-new/package.json | 36 -- libs/config-new/src/index.ts | 127 ----- libs/config-new/tsconfig.json | 22 - libs/config/.env.example | 93 ++-- libs/config/.env.production.example | 28 -- libs/config/README.md | 346 ++++++++++---- libs/config/USAGE.md | 131 ------ libs/config/bunfig.toml | 15 - .../config/default.json | 0 .../config/development.json | 0 .../config/production.json | 0 libs/{config-new => config}/config/test.json | 0 libs/config/package.json | 58 +-- libs/config/setup.bat | 24 - libs/config/src/admin-interfaces.ts | 118 ----- libs/{config-new => config}/src/cli.ts | 0 .../src/config-manager.ts | 0 libs/config/src/core.ts | 63 --- libs/config/src/data-providers.ts | 185 -------- libs/config/src/database.ts | 56 --- libs/config/src/dragonfly.ts | 81 ---- libs/config/src/env-utils.ts | 165 ------- libs/{config-new => config}/src/errors.ts | 0 libs/config/src/index.ts | 145 +++++- .../src/loaders/env.loader.ts | 0 .../src/loaders/file.loader.ts | 0 libs/config/src/logging.ts | 74 --- libs/config/src/loki.ts | 63 --- libs/config/src/mongodb.ts | 77 --- libs/config/src/monitoring.ts | 92 ---- libs/config/src/postgres.ts | 56 --- libs/config/src/questdb.ts | 55 --- libs/config/src/risk.ts | 80 ---- .../src/schemas/base.schema.ts | 0 .../src/schemas/database.schema.ts | 0 .../src/schemas/index.ts | 0 .../src/schemas/provider.schema.ts | 0 .../src/schemas/service.schema.ts | 0 .../{config-new => config}/src/types/index.ts | 0 .../src/utils/secrets.ts | 0 .../src/utils/validation.ts | 0 libs/config/test-config.mjs | 85 ---- .../test/config-manager.test.ts | 0 .../{config-new => config}/test/index.test.ts | 0 libs/config/test/integration.test.ts | 445 ------------------ .../test/loaders.test.ts | 0 libs/config/test/setup.ts | 93 ---- libs/config/tsconfig.json | 29 +- libs/config/turbo.json | 19 - libs/config/validate-config.js | 118 ----- libs/queue/src/batch-processor.ts | 34 +- libs/queue/src/index.ts | 13 + libs/queue/src/queue-factory.ts | 112 +++++ libs/queue/src/queue-instance.ts | 186 ++++++++ libs/queue/src/queue-manager.ts | 7 + scripts/build-libs.sh | 2 +- tsconfig.json | 6 +- 70 files changed, 889 insertions(+), 2978 deletions(-) delete mode 100644 libs/config-new/.env.example delete mode 100644 libs/config-new/README.md delete mode 100644 libs/config-new/package.json delete mode 100644 libs/config-new/src/index.ts delete mode 100644 libs/config-new/tsconfig.json delete mode 100644 libs/config/.env.production.example delete mode 100644 libs/config/USAGE.md delete mode 100644 libs/config/bunfig.toml rename libs/{config-new => config}/config/default.json (100%) rename libs/{config-new => config}/config/development.json (100%) rename libs/{config-new => config}/config/production.json (100%) rename libs/{config-new => config}/config/test.json (100%) delete mode 100644 libs/config/setup.bat delete mode 100644 libs/config/src/admin-interfaces.ts rename libs/{config-new => config}/src/cli.ts (100%) rename libs/{config-new => config}/src/config-manager.ts (100%) delete mode 100644 libs/config/src/core.ts delete mode 100644 libs/config/src/data-providers.ts delete mode 100644 libs/config/src/database.ts delete mode 100644 libs/config/src/dragonfly.ts delete mode 100644 libs/config/src/env-utils.ts rename libs/{config-new => config}/src/errors.ts (100%) rename libs/{config-new => config}/src/loaders/env.loader.ts (100%) rename libs/{config-new => config}/src/loaders/file.loader.ts (100%) delete mode 100644 libs/config/src/logging.ts delete mode 100644 libs/config/src/loki.ts delete mode 100644 libs/config/src/mongodb.ts delete mode 100644 libs/config/src/monitoring.ts delete mode 100644 libs/config/src/postgres.ts delete mode 100644 libs/config/src/questdb.ts delete mode 100644 libs/config/src/risk.ts rename libs/{config-new => config}/src/schemas/base.schema.ts (100%) rename libs/{config-new => config}/src/schemas/database.schema.ts (100%) rename libs/{config-new => config}/src/schemas/index.ts (100%) rename libs/{config-new => config}/src/schemas/provider.schema.ts (100%) rename libs/{config-new => config}/src/schemas/service.schema.ts (100%) rename libs/{config-new => config}/src/types/index.ts (100%) rename libs/{config-new => config}/src/utils/secrets.ts (100%) rename libs/{config-new => config}/src/utils/validation.ts (100%) delete mode 100644 libs/config/test-config.mjs rename libs/{config-new => config}/test/config-manager.test.ts (100%) rename libs/{config-new => config}/test/index.test.ts (100%) delete mode 100644 libs/config/test/integration.test.ts rename libs/{config-new => config}/test/loaders.test.ts (100%) delete mode 100644 libs/config/test/setup.ts delete mode 100644 libs/config/turbo.json delete mode 100644 libs/config/validate-config.js create mode 100644 libs/queue/src/queue-factory.ts create mode 100644 libs/queue/src/queue-instance.ts diff --git a/apps/data-service/package.json b/apps/data-service/package.json index a0e3e58..36a70c7 100644 --- a/apps/data-service/package.json +++ b/apps/data-service/package.json @@ -12,11 +12,13 @@ "clean": "rm -rf dist" }, "dependencies": { - "@stock-bot/config-new": "*", + "@stock-bot/cache": "*", + "@stock-bot/config": "*", "@stock-bot/logger": "*", "@stock-bot/mongodb-client": "*", "@stock-bot/postgres-client": "*", "@stock-bot/questdb-client": "*", + "@stock-bot/queue": "*", "@stock-bot/shutdown": "*", "hono": "^4.0.0" }, diff --git a/apps/data-service/src/index.ts b/apps/data-service/src/index.ts index dbc19ee..d9833b4 100644 --- a/apps/data-service/src/index.ts +++ b/apps/data-service/src/index.ts @@ -5,14 +5,13 @@ // Framework imports import { Hono } from 'hono'; import { cors } from 'hono/cors'; - // Library imports -import { initializeServiceConfig } from '@stock-bot/config-new'; +import { initializeServiceConfig } from '@stock-bot/config'; import { getLogger, setLoggerConfig, shutdownLoggers } from '@stock-bot/logger'; import { createAndConnectMongoDBClient, MongoDBClient } from '@stock-bot/mongodb-client'; import { createAndConnectPostgreSQLClient, PostgreSQLClient } from '@stock-bot/postgres-client'; +import { getQueue, initializeQueueSystem, shutdownAllQueues } from '@stock-bot/queue'; import { Shutdown } from '@stock-bot/shutdown'; - // Local imports import { exchangeRoutes, healthRoutes, queueRoutes } from './routes'; @@ -20,6 +19,7 @@ import { exchangeRoutes, healthRoutes, queueRoutes } from './routes'; const config = await initializeServiceConfig(); const serviceConfig = config.service; const databaseConfig = config.database; +const queueConfig = config.queue; // Initialize logger with config const loggingConfig = config.logging; @@ -50,6 +50,7 @@ const PORT = serviceConfig.port; let server: ReturnType | null = null; let postgresClient: PostgreSQLClient | null = null; let mongoClient: MongoDBClient | null = null; +// Queue system will be initialized globally // Initialize shutdown manager const shutdown = Shutdown.getInstance({ timeout: 15000 }); @@ -97,6 +98,16 @@ async function initializeServices() { }); logger.info('PostgreSQL connected'); + // Initialize queue system + logger.info('Initializing queue system...'); + await initializeQueueSystem({ + redis: queueConfig.redis, + defaultJobOptions: queueConfig.defaultJobOptions, + workers: 5, + concurrency: 20, + }); + logger.info('Queue system initialized'); + logger.info('All services initialized successfully'); } catch (error) { logger.error('Failed to initialize services', { error }); @@ -130,6 +141,16 @@ shutdown.onShutdown(async () => { } }); +shutdown.onShutdown(async () => { + logger.info('Shutting down queue system...'); + try { + await shutdownAllQueues(); + logger.info('Queue system shut down'); + } catch (error) { + logger.error('Error shutting down queue system', { error }); + } +}); + shutdown.onShutdown(async () => { logger.info('Disconnecting from databases...'); try { @@ -160,4 +181,7 @@ startServer().catch(error => { process.exit(1); }); -logger.info('Data service startup initiated'); \ No newline at end of file +logger.info('Data service startup initiated'); + +// Export queue functions for providers +export { getQueue }; diff --git a/apps/data-service/tsconfig.json b/apps/data-service/tsconfig.json index b8e325e..42b4ae1 100644 --- a/apps/data-service/tsconfig.json +++ b/apps/data-service/tsconfig.json @@ -16,8 +16,10 @@ ], "references": [ { "path": "../../libs/types" }, - { "path": "../../libs/config-new" }, + { "path": "../../libs/config" }, { "path": "../../libs/logger" }, + { "path": "../../libs/cache" }, + { "path": "../../libs/queue" }, { "path": "../../libs/mongodb-client" }, { "path": "../../libs/postgres-client" }, { "path": "../../libs/questdb-client" }, diff --git a/apps/data-service/turbo.json b/apps/data-service/turbo.json index be12598..79e1480 100644 --- a/apps/data-service/turbo.json +++ b/apps/data-service/turbo.json @@ -3,8 +3,10 @@ "tasks": { "build": { "dependsOn": [ - "@stock-bot/config-new#build", + "@stock-bot/config#build", "@stock-bot/logger#build", + "@stock-bot/cache#build", + "@stock-bot/queue#build", "@stock-bot/mongodb-client#build", "@stock-bot/postgres-client#build", "@stock-bot/questdb-client#build", diff --git a/apps/data-sync-service/package.json b/apps/data-sync-service/package.json index 3d2b614..8e0bebf 100644 --- a/apps/data-sync-service/package.json +++ b/apps/data-sync-service/package.json @@ -12,7 +12,7 @@ "clean": "rm -rf dist" }, "dependencies": { - "@stock-bot/config-new": "*", + "@stock-bot/config": "*", "@stock-bot/logger": "*", "@stock-bot/mongodb-client": "*", "@stock-bot/postgres-client": "*", diff --git a/apps/data-sync-service/src/index.ts b/apps/data-sync-service/src/index.ts index 2d2dcf6..ab5bf53 100644 --- a/apps/data-sync-service/src/index.ts +++ b/apps/data-sync-service/src/index.ts @@ -5,19 +5,17 @@ // Framework imports import { Hono } from 'hono'; import { cors } from 'hono/cors'; - // Library imports -import { initializeServiceConfig } from '@stock-bot/config-new'; +import { initializeServiceConfig } from '@stock-bot/config'; import { getLogger, setLoggerConfig, shutdownLoggers } from '@stock-bot/logger'; import { createAndConnectMongoDBClient, MongoDBClient } from '@stock-bot/mongodb-client'; import { createAndConnectPostgreSQLClient, PostgreSQLClient } from '@stock-bot/postgres-client'; import { Shutdown } from '@stock-bot/shutdown'; - -// Local imports import { enhancedSyncManager } from './services/enhanced-sync-manager'; import { syncManager } from './services/sync-manager'; +// Local imports import { setMongoDBClient, setPostgreSQLClient } from './clients'; -import { healthRoutes, syncRoutes, enhancedSyncRoutes, statsRoutes } from './routes'; +import { enhancedSyncRoutes, healthRoutes, statsRoutes, syncRoutes } from './routes'; // Initialize configuration with automatic monorepo config inheritance const config = await initializeServiceConfig(); diff --git a/apps/data-sync-service/tsconfig.json b/apps/data-sync-service/tsconfig.json index 0f7cff9..42b4ae1 100644 --- a/apps/data-sync-service/tsconfig.json +++ b/apps/data-sync-service/tsconfig.json @@ -1,22 +1,28 @@ { + "extends": "../../tsconfig.json", "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 + "rootDir": "./src" }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.test.ts"] + "exclude": [ + "node_modules", + "dist", + "**/*.test.ts", + "**/*.spec.ts", + "**/test/**", + "**/tests/**", + "**/__tests__/**" + ], + "references": [ + { "path": "../../libs/types" }, + { "path": "../../libs/config" }, + { "path": "../../libs/logger" }, + { "path": "../../libs/cache" }, + { "path": "../../libs/queue" }, + { "path": "../../libs/mongodb-client" }, + { "path": "../../libs/postgres-client" }, + { "path": "../../libs/questdb-client" }, + { "path": "../../libs/shutdown" } + ] } diff --git a/apps/web-api/package.json b/apps/web-api/package.json index f5c9d4b..9fa41b1 100644 --- a/apps/web-api/package.json +++ b/apps/web-api/package.json @@ -12,7 +12,7 @@ "clean": "rm -rf dist" }, "dependencies": { - "@stock-bot/config-new": "*", + "@stock-bot/config": "*", "@stock-bot/logger": "*", "@stock-bot/mongodb-client": "*", "@stock-bot/postgres-client": "*", diff --git a/apps/web-api/src/index.ts b/apps/web-api/src/index.ts index d3c5fdd..e4e4355 100644 --- a/apps/web-api/src/index.ts +++ b/apps/web-api/src/index.ts @@ -3,15 +3,15 @@ */ import { Hono } from 'hono'; import { cors } from 'hono/cors'; -import { initializeServiceConfig } from '@stock-bot/config-new'; -import { getLogger, shutdownLoggers, setLoggerConfig } from '@stock-bot/logger'; +import { initializeServiceConfig } from '@stock-bot/config'; +import { getLogger, setLoggerConfig, shutdownLoggers } from '@stock-bot/logger'; import { createAndConnectMongoDBClient, MongoDBClient } from '@stock-bot/mongodb-client'; import { createAndConnectPostgreSQLClient, PostgreSQLClient } from '@stock-bot/postgres-client'; import { Shutdown } from '@stock-bot/shutdown'; -import { setPostgreSQLClient, setMongoDBClient } from './clients'; // Import routes import { exchangeRoutes } from './routes/exchange.routes'; import { healthRoutes } from './routes/health.routes'; +import { setMongoDBClient, setPostgreSQLClient } from './clients'; // Initialize configuration with automatic monorepo config inheritance const config = await initializeServiceConfig(); diff --git a/bun.lock b/bun.lock index 4e94000..2defcd6 100644 --- a/bun.lock +++ b/bun.lock @@ -47,22 +47,16 @@ "version": "1.0.0", "dependencies": { "@stock-bot/cache": "*", - "@stock-bot/event-bus": "*", - "@stock-bot/http": "*", + "@stock-bot/config": "*", "@stock-bot/logger": "*", "@stock-bot/mongodb-client": "*", + "@stock-bot/postgres-client": "*", "@stock-bot/questdb-client": "*", "@stock-bot/queue": "*", "@stock-bot/shutdown": "*", - "@stock-bot/types": "*", - "chromium-bidi": "^5.3.1", - "electron": "^36.4.0", "hono": "^4.0.0", - "p-limit": "^6.2.0", - "ws": "^8.0.0", }, "devDependencies": { - "@types/ws": "^8.0.0", "typescript": "^5.0.0", }, }, @@ -70,6 +64,7 @@ "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": "*", @@ -84,7 +79,7 @@ "name": "@stock-bot/web-api", "version": "1.0.0", "dependencies": { - "@stock-bot/config-new": "*", + "@stock-bot/config": "*", "@stock-bot/logger": "*", "@stock-bot/mongodb-client": "*", "@stock-bot/postgres-client": "*", @@ -176,8 +171,8 @@ "typescript": "^5.3.0", }, }, - "libs/config-new": { - "name": "@stock-bot/config-new", + "libs/config": { + "name": "@stock-bot/config", "version": "1.0.0", "bin": { "config-cli": "./dist/cli.js", @@ -519,8 +514,6 @@ "@balena/dockerignore": ["@balena/dockerignore@1.0.2", "", {}, "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="], - "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], "@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], @@ -829,7 +822,7 @@ "@stock-bot/config": ["@stock-bot/config@workspace:libs/config"], - "@stock-bot/config-new": ["@stock-bot/config-new@workspace:libs/config-new"], + "@stock-bot/config": ["@stock-bot/config@workspace:libs/config"], "@stock-bot/data-frame": ["@stock-bot/data-frame@workspace:libs/data-frame"], @@ -895,8 +888,6 @@ "@types/bun": ["@types/bun@1.2.15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="], - "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], - "@types/cookiejar": ["@types/cookiejar@2.1.5", "", {}, "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q=="], "@types/docker-modem": ["@types/docker-modem@3.0.6", "", { "dependencies": { "@types/node": "*", "@types/ssh2": "*" } }, "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg=="], @@ -913,8 +904,6 @@ "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], - "@types/keyv": ["@types/keyv@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg=="], - "@types/methods": ["@types/methods@1.1.4", "", {}, "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ=="], "@types/mongodb": ["@types/mongodb@4.0.7", "", { "dependencies": { "mongodb": "*" } }, "sha512-lPUYPpzA43baXqnd36cZ9xxorprybxXDzteVKCPAdp14ppHtFJHnXYvNpmBvtMUTb5fKXVv6sVbzo1LHkWhJlw=="], @@ -937,8 +926,6 @@ "@types/react-window": ["@types/react-window@1.8.8", "", { "dependencies": { "@types/react": "*" } }, "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q=="], - "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="], - "@types/semver": ["@types/semver@7.7.0", "", {}, "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA=="], "@types/ssh2": ["@types/ssh2@0.5.52", "", { "dependencies": { "@types/node": "*", "@types/ssh2-streams": "*" } }, "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg=="], @@ -955,10 +942,6 @@ "@types/whatwg-url": ["@types/whatwg-url@11.0.5", "", { "dependencies": { "@types/webidl-conversions": "*" } }, "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ=="], - "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - - "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], - "@types/yup": ["@types/yup@0.32.0", "", { "dependencies": { "yup": "*" } }, "sha512-Gr2lllWTDxGVYHgWfL8szjdedERpNgm44L9BDL2cmcHG7Bfd6taEpiW3ayMFLaYvlJr/6bFXDJdh6L406AGlFg=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.34.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.34.0", "@typescript-eslint/type-utils": "8.34.0", "@typescript-eslint/utils": "8.34.0", "@typescript-eslint/visitor-keys": "8.34.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.34.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w=="], @@ -1089,8 +1072,6 @@ "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=="], @@ -1145,16 +1126,12 @@ "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], - "chromium-bidi": ["chromium-bidi@5.3.1", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-fbkgn0/m6RIRknVEez+QOYuvukUomBC0XnS8fgdbl9FeunjR3vUvPN6iYrbzXIuaJXYOwGU8FZgOTDzBImGvLw=="], - "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=="], - "clone-response": ["clone-response@1.0.3", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA=="], - "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], @@ -1253,10 +1230,6 @@ "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], - "detect-node": ["detect-node@2.1.0", "", {}, "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="], - - "devtools-protocol": ["devtools-protocol@0.0.1473885", "", {}, "sha512-J4UUrUc6r5VZXT7xuPEoHpF+vd1MKBtlAUjPnjXZtt1HgG1ccgUzdMc20COqGtWoSKwk/W9DwJ/GUMs1GpnqnA=="], - "dezalgo": ["dezalgo@1.0.4", "", { "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig=="], "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], @@ -1283,8 +1256,6 @@ "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=="], @@ -1295,8 +1266,6 @@ "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], - "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], - "es-abstract": ["es-abstract@1.24.0", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], @@ -1313,8 +1282,6 @@ "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], - "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="], - "esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -1383,8 +1350,6 @@ "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=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -1405,8 +1370,6 @@ "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=="], @@ -1447,8 +1410,6 @@ "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], - "fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], - "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], @@ -1471,7 +1432,7 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], + "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "get-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=="], @@ -1483,8 +1444,6 @@ "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - "global-agent": ["global-agent@3.0.0", "", { "dependencies": { "boolean": "^3.0.1", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", "semver": "^7.3.2", "serialize-error": "^7.0.1" } }, "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q=="], - "globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], @@ -1659,12 +1618,8 @@ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], - "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], - "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], - "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], - "jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="], "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], @@ -1709,8 +1664,6 @@ "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=="], @@ -1745,8 +1698,6 @@ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], - "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], @@ -1871,7 +1822,7 @@ "p-cancelable": ["p-cancelable@4.0.1", "", {}, "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg=="], - "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], @@ -2091,8 +2042,6 @@ "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], - "roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="], - "rollup": ["rollup@3.29.5", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w=="], "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=="], @@ -2121,12 +2070,8 @@ "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], - "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=="], @@ -2225,8 +2170,6 @@ "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], - "sumchecker": ["sumchecker@3.0.1", "", { "dependencies": { "debug": "^4.1.0" } }, "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="], - "superagent": ["superagent@8.1.2", "", { "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.4", "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", "formidable": "^2.1.2", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0", "semver": "^7.3.8" } }, "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA=="], "supertest": ["supertest@6.3.4", "", { "dependencies": { "methods": "^1.1.2", "superagent": "^8.1.2" } }, "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw=="], @@ -2319,8 +2262,6 @@ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "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=="], @@ -2367,8 +2308,6 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], - "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], @@ -2383,7 +2322,7 @@ "yauzl": ["yauzl@3.2.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "pend": "~1.2.0" } }, "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w=="], - "yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "yup": ["yup@1.6.1", "", { "dependencies": { "property-expr": "^2.0.5", "tiny-case": "^1.0.3", "toposort": "^2.0.2", "type-fest": "^2.19.0" } }, "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA=="], @@ -2409,10 +2348,6 @@ "@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], - "@electron/get/got": ["got@11.8.6", "", { "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", "http2-wrapper": "^1.0.0-beta.5.2", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" } }, "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g=="], - - "@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], @@ -2445,7 +2380,7 @@ "@stock-bot/config/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/config-new/@types/node": ["@types/node@20.19.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q=="], + "@stock-bot/config/@types/node": ["@types/node@20.19.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q=="], "@stock-bot/data-frame/@types/node": ["@types/node@20.19.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q=="], @@ -2517,14 +2452,10 @@ "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=="], - "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], - "compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], "decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], @@ -2565,8 +2496,6 @@ "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=="], @@ -2607,8 +2536,6 @@ "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=="], @@ -2629,8 +2556,6 @@ "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=="], "tailwindcss/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], @@ -2651,22 +2576,6 @@ "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - "@electron/get/got/@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], - - "@electron/get/got/@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="], - - "@electron/get/got/cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="], - - "@electron/get/got/cacheable-request": ["cacheable-request@7.0.4", "", { "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", "normalize-url": "^6.0.1", "responselike": "^2.0.0" } }, "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg=="], - - "@electron/get/got/http2-wrapper": ["http2-wrapper@1.0.3", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg=="], - - "@electron/get/got/lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="], - - "@electron/get/got/p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], - - "@electron/get/got/responselike": ["responselike@2.0.1", "", { "dependencies": { "lowercase-keys": "^2.0.0" } }, "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw=="], - "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], @@ -2923,8 +2832,6 @@ "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=="], "lazystream/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], @@ -2941,8 +2848,6 @@ "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=="], - "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "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=="], @@ -2969,8 +2874,6 @@ "@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=="], diff --git a/config/default.json b/config/default.json index bba0511..4a2d753 100644 --- a/config/default.json +++ b/config/default.json @@ -74,6 +74,22 @@ "baseUrl": "https://query1.finance.yahoo.com" } }, + "queue": { + "redis": { + "host": "localhost", + "port": 6379, + "db": 1 + }, + "defaultJobOptions": { + "attempts": 3, + "backoff": { + "type": "exponential", + "delay": 1000 + }, + "removeOnComplete": true, + "removeOnFail": false + } + }, "features": { "realtime": true, "backtesting": true, diff --git a/libs/config-new/.env.example b/libs/config-new/.env.example deleted file mode 100644 index 135c4e4..0000000 --- a/libs/config-new/.env.example +++ /dev/null @@ -1,50 +0,0 @@ -# Environment -NODE_ENV=development - -# Service Configuration -STOCKBOT_SERVICE_NAME=stock-bot-service -STOCKBOT_SERVICE_PORT=3000 - -# Database Configuration -STOCKBOT_DATABASE_POSTGRES_HOST=localhost -STOCKBOT_DATABASE_POSTGRES_PORT=5432 -STOCKBOT_DATABASE_POSTGRES_DATABASE=stockbot -STOCKBOT_DATABASE_POSTGRES_USER=postgres -STOCKBOT_DATABASE_POSTGRES_PASSWORD=postgres - -STOCKBOT_DATABASE_QUESTDB_HOST=localhost -STOCKBOT_DATABASE_QUESTDB_ILP_PORT=9009 -STOCKBOT_DATABASE_QUESTDB_HTTP_PORT=9000 - -STOCKBOT_DATABASE_MONGODB_HOST=localhost -STOCKBOT_DATABASE_MONGODB_PORT=27017 -STOCKBOT_DATABASE_MONGODB_DATABASE=stockbot - -STOCKBOT_DATABASE_DRAGONFLY_HOST=localhost -STOCKBOT_DATABASE_DRAGONFLY_PORT=6379 - -# Provider Configuration -STOCKBOT_PROVIDERS_EOD_API_KEY=your_eod_api_key -STOCKBOT_PROVIDERS_EOD_ENABLED=true - -STOCKBOT_PROVIDERS_IB_ENABLED=false -STOCKBOT_PROVIDERS_IB_GATEWAY_HOST=localhost -STOCKBOT_PROVIDERS_IB_GATEWAY_PORT=5000 -STOCKBOT_PROVIDERS_IB_ACCOUNT=your_account_id - -STOCKBOT_PROVIDERS_QM_ENABLED=false -STOCKBOT_PROVIDERS_QM_USERNAME=your_username -STOCKBOT_PROVIDERS_QM_PASSWORD=your_password -STOCKBOT_PROVIDERS_QM_WEBMASTER_ID=your_webmaster_id - -# Logging -STOCKBOT_LOGGING_LEVEL=info -STOCKBOT_LOGGING_LOKI_ENABLED=false -STOCKBOT_LOGGING_LOKI_HOST=localhost -STOCKBOT_LOGGING_LOKI_PORT=3100 - -# HTTP Proxy (optional) -STOCKBOT_HTTP_PROXY_ENABLED=false -STOCKBOT_HTTP_PROXY_URL=http://proxy.example.com:8080 -STOCKBOT_HTTP_PROXY_AUTH_USERNAME=username -STOCKBOT_HTTP_PROXY_AUTH_PASSWORD=password \ No newline at end of file diff --git a/libs/config-new/README.md b/libs/config-new/README.md deleted file mode 100644 index be5c83e..0000000 --- a/libs/config-new/README.md +++ /dev/null @@ -1,243 +0,0 @@ -# @stock-bot/config-new - -A robust, type-safe configuration library for the Stock Bot application. Built with Zod for validation and supports multiple configuration sources with proper precedence. - -## Features - -- **Type-safe configuration** with Zod schemas -- **Multiple configuration sources**: JSON files and environment variables -- **Environment-specific overrides** (development, test, production) -- **Dynamic provider configurations** -- **No circular dependencies** - designed to be used by all other libraries -- **Clear error messages** with validation -- **Runtime configuration updates** (useful for testing) -- **Singleton pattern** for global configuration access - -## Installation - -```bash -bun add @stock-bot/config-new -``` - -## Usage - -### Basic Usage - -```typescript -import { initializeConfig, getConfig } from '@stock-bot/config-new'; - -// Initialize configuration (call once at app startup) -await initializeConfig(); - -// Get configuration -const config = getConfig(); -console.log(config.database.postgres.host); - -// Use convenience functions -import { getDatabaseConfig, isProduction } from '@stock-bot/config-new'; - -const dbConfig = getDatabaseConfig(); -if (isProduction()) { - // Production-specific logic -} -``` - -### Custom Configuration - -```typescript -import { ConfigManager } from '@stock-bot/config-new'; -import { z } from 'zod'; - -// Define your schema -const myConfigSchema = z.object({ - app: z.object({ - name: z.string(), - version: z.string(), - }), - features: z.object({ - enableBeta: z.boolean().default(false), - }), -}); - -// Create config manager -const configManager = new ConfigManager({ - configPath: './my-config', -}); - -// Initialize with schema -const config = await configManager.initialize(myConfigSchema); -``` - -### Provider-Specific Configuration - -```typescript -import { getProviderConfig } from '@stock-bot/config-new'; - -// Get provider configuration -const eodConfig = getProviderConfig('eod'); -console.log(eodConfig.apiKey); - -// Check if provider is enabled -if (eodConfig.enabled) { - // Use EOD provider -} -``` - -### Environment Variables - -Environment variables are loaded with the `STOCKBOT_` prefix and follow a naming convention: - -```bash -# Database configuration -STOCKBOT_DATABASE_POSTGRES_HOST=localhost -STOCKBOT_DATABASE_POSTGRES_PORT=5432 - -# Provider configuration -STOCKBOT_PROVIDERS_EOD_API_KEY=your_api_key -STOCKBOT_PROVIDERS_EOD_ENABLED=true - -# Service configuration -STOCKBOT_SERVICE_PORT=3000 -STOCKBOT_LOGGING_LEVEL=debug -``` - -### Configuration Precedence - -Configuration is loaded in the following order (later sources override earlier ones): - -1. `config/default.json` - Base configuration -2. `config/{environment}.json` - Environment-specific overrides -3. Environment variables - Highest priority - -### Advanced Usage - -```typescript -import { getConfigManager } from '@stock-bot/config-new'; - -const manager = getConfigManager(); - -// Get specific value by path -const port = manager.getValue('service.port'); - -// Check if configuration exists -if (manager.has('providers.ib')) { - // IB provider is configured -} - -// Update configuration at runtime (useful for testing) -manager.set({ - logging: { - level: 'debug' - } -}); - -// Create typed getter -const getQueueConfig = manager.createTypedGetter(queueConfigSchema); -const queueConfig = getQueueConfig(); -``` - -## Configuration Schema - -The library provides pre-defined schemas for common configurations: - -### Base Configuration -- `environment` - Current environment (development/test/production) -- `name` - Application name -- `version` - Application version -- `debug` - Debug mode flag - -### Service Configuration -- `name` - Service name -- `port` - Service port -- `host` - Service host -- `healthCheckPath` - Health check endpoint -- `cors` - CORS configuration - -### Database Configuration -- `postgres` - PostgreSQL configuration -- `questdb` - QuestDB configuration -- `mongodb` - MongoDB configuration -- `dragonfly` - Dragonfly/Redis configuration - -### Provider Configuration -- `eod` - EOD Historical Data provider -- `ib` - Interactive Brokers provider -- `qm` - QuoteMedia provider -- `yahoo` - Yahoo Finance provider - -## Testing - -```typescript -import { resetConfig, initializeConfig } from '@stock-bot/config-new'; - -beforeEach(() => { - resetConfig(); -}); - -test('custom config', async () => { - process.env.NODE_ENV = 'test'; - process.env.STOCKBOT_SERVICE_PORT = '4000'; - - await initializeConfig(); - const config = getConfig(); - - expect(config.service.port).toBe(4000); -}); -``` - -## Custom Loaders - -You can create custom configuration loaders: - -```typescript -import { ConfigLoader } from '@stock-bot/config-new'; - -class ApiConfigLoader implements ConfigLoader { - readonly priority = 75; // Between file (50) and env (100) - - async load(): Promise> { - // Fetch configuration from API - const response = await fetch('https://api.example.com/config'); - return response.json(); - } -} - -// Use custom loader -const configManager = new ConfigManager({ - loaders: [ - new FileLoader('./config', 'production'), - new ApiConfigLoader(), - new EnvLoader('STOCKBOT_'), - ], -}); -``` - -## Error Handling - -The library provides specific error types: - -```typescript -import { ConfigError, ConfigValidationError } from '@stock-bot/config-new'; - -try { - await initializeConfig(); -} catch (error) { - if (error instanceof ConfigValidationError) { - console.error('Validation failed:', error.errors); - } else if (error instanceof ConfigError) { - console.error('Configuration error:', error.message); - } -} -``` - -## Best Practices - -1. **Initialize once**: Call `initializeConfig()` once at application startup -2. **Use schemas**: Always define and validate configurations with Zod schemas -3. **Environment variables**: Use the `STOCKBOT_` prefix for all env vars -4. **Type safety**: Leverage TypeScript types from the schemas -5. **Testing**: Reset configuration between tests with `resetConfig()` - -## License - -MIT \ No newline at end of file diff --git a/libs/config-new/package.json b/libs/config-new/package.json deleted file mode 100644 index 98ec227..0000000 --- a/libs/config-new/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@stock-bot/config-new", - "version": "1.0.0", - "type": "module", - "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" - } - }, - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "test": "bun test", - "clean": "rm -rf dist", - "cli": "bun run src/cli.ts", - "validate": "bun run src/cli.ts --validate", - "check": "bun run src/cli.ts --check" - }, - "bin": { - "config-cli": "./dist/cli.js" - }, - "dependencies": { - "zod": "^3.22.4" - }, - "devDependencies": { - "@types/bun": "^1.0.0", - "@types/node": "^20.10.5", - "typescript": "^5.3.3" - }, - "peerDependencies": { - "bun": "^1.0.0" - } -} \ No newline at end of file diff --git a/libs/config-new/src/index.ts b/libs/config-new/src/index.ts deleted file mode 100644 index 41da483..0000000 --- a/libs/config-new/src/index.ts +++ /dev/null @@ -1,127 +0,0 @@ -// Export all schemas -export * from './schemas'; - -// Export types -export * from './types'; - -// Export errors -export * from './errors'; - -// Export loaders -export { EnvLoader } from './loaders/env.loader'; -export { FileLoader } from './loaders/file.loader'; - -// Export ConfigManager -export { ConfigManager } from './config-manager'; - -// Export utilities -export * from './utils/secrets'; -export * from './utils/validation'; - -// Import necessary types for singleton -import { ConfigManager } from './config-manager'; -import { AppConfig, appConfigSchema } from './schemas'; -import { FileLoader } from './loaders/file.loader'; -import { EnvLoader } from './loaders/env.loader'; - -// Create singleton instance -let configInstance: ConfigManager | null = null; - -/** - * Initialize the global configuration - */ -export async function initializeConfig( - configPath?: string -): Promise { - if (!configInstance) { - configInstance = new ConfigManager({ - configPath, - }); - } - return configInstance.initialize(appConfigSchema); -} - -/** - * Initialize configuration for a service in a monorepo - * Automatically loads configs from: - * 1. Root config directory (../../config) - * 2. Service-specific config directory (./config) - * 3. Environment variables - */ -export async function initializeServiceConfig(): Promise { - if (!configInstance) { - const environment = process.env.NODE_ENV || 'development'; - configInstance = new ConfigManager({ - loaders: [ - new FileLoader('../../config', environment), // Root config - new FileLoader('./config', environment), // Service config - new EnvLoader(''), // Environment variables - ] - }); - } - return configInstance.initialize(appConfigSchema); -} - -/** - * Get the current configuration - */ -export function getConfig(): AppConfig { - if (!configInstance) { - throw new Error('Configuration not initialized. Call initializeConfig() first.'); - } - return configInstance.get(); -} - -/** - * Get configuration manager instance - */ -export function getConfigManager(): ConfigManager { - if (!configInstance) { - throw new Error('Configuration not initialized. Call initializeConfig() first.'); - } - return configInstance; -} - -/** - * Reset configuration (useful for testing) - */ -export function resetConfig(): void { - if (configInstance) { - configInstance.reset(); - configInstance = null; - } -} - -// Export convenience functions for common configs -export function getDatabaseConfig() { - return getConfig().database; -} - -export function getServiceConfig() { - return getConfig().service; -} - -export function getLoggingConfig() { - return getConfig().logging; -} - -export function getProviderConfig(provider: string) { - const providers = getConfig().providers; - if (!providers || !(provider in providers)) { - throw new Error(`Provider configuration not found: ${provider}`); - } - return (providers as any)[provider]; -} - -// Export environment helpers -export function isDevelopment(): boolean { - return getConfig().environment === 'development'; -} - -export function isProduction(): boolean { - return getConfig().environment === 'production'; -} - -export function isTest(): boolean { - return getConfig().environment === 'test'; -} \ No newline at end of file diff --git a/libs/config-new/tsconfig.json b/libs/config-new/tsconfig.json deleted file mode 100644 index 64c38b3..0000000 --- a/libs/config-new/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "lib": ["ES2022"], - "moduleResolution": "bundler", - "types": ["bun-types"], - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "declaration": true, - "declarationMap": true, - "outDir": "./dist", - "rootDir": "./src", - "declarationDir": "./dist", - "composite": true, - "tsBuildInfoFile": "./tsconfig.tsbuildinfo" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "test"] -} \ No newline at end of file diff --git a/libs/config/.env.example b/libs/config/.env.example index 1100547..135c4e4 100644 --- a/libs/config/.env.example +++ b/libs/config/.env.example @@ -1,53 +1,50 @@ -# Base environment variables for Stock Bot - # Environment NODE_ENV=development +# Service Configuration +STOCKBOT_SERVICE_NAME=stock-bot-service +STOCKBOT_SERVICE_PORT=3000 + +# Database Configuration +STOCKBOT_DATABASE_POSTGRES_HOST=localhost +STOCKBOT_DATABASE_POSTGRES_PORT=5432 +STOCKBOT_DATABASE_POSTGRES_DATABASE=stockbot +STOCKBOT_DATABASE_POSTGRES_USER=postgres +STOCKBOT_DATABASE_POSTGRES_PASSWORD=postgres + +STOCKBOT_DATABASE_QUESTDB_HOST=localhost +STOCKBOT_DATABASE_QUESTDB_ILP_PORT=9009 +STOCKBOT_DATABASE_QUESTDB_HTTP_PORT=9000 + +STOCKBOT_DATABASE_MONGODB_HOST=localhost +STOCKBOT_DATABASE_MONGODB_PORT=27017 +STOCKBOT_DATABASE_MONGODB_DATABASE=stockbot + +STOCKBOT_DATABASE_DRAGONFLY_HOST=localhost +STOCKBOT_DATABASE_DRAGONFLY_PORT=6379 + +# Provider Configuration +STOCKBOT_PROVIDERS_EOD_API_KEY=your_eod_api_key +STOCKBOT_PROVIDERS_EOD_ENABLED=true + +STOCKBOT_PROVIDERS_IB_ENABLED=false +STOCKBOT_PROVIDERS_IB_GATEWAY_HOST=localhost +STOCKBOT_PROVIDERS_IB_GATEWAY_PORT=5000 +STOCKBOT_PROVIDERS_IB_ACCOUNT=your_account_id + +STOCKBOT_PROVIDERS_QM_ENABLED=false +STOCKBOT_PROVIDERS_QM_USERNAME=your_username +STOCKBOT_PROVIDERS_QM_PASSWORD=your_password +STOCKBOT_PROVIDERS_QM_WEBMASTER_ID=your_webmaster_id + # Logging -LOG_LEVEL=debug +STOCKBOT_LOGGING_LEVEL=info +STOCKBOT_LOGGING_LOKI_ENABLED=false +STOCKBOT_LOGGING_LOKI_HOST=localhost +STOCKBOT_LOGGING_LOKI_PORT=3100 -# Database configuration -DRAGONFLY_HOST=localhost -DRAGONFLY_PORT=6379 -DRAGONFLY_PASSWORD= -DRAGONFLY_MAX_RETRIES_PER_REQUEST=3 - -TIMESCALE_HOST=localhost -TIMESCALE_PORT=5432 -TIMESCALE_DB=stockbot -TIMESCALE_USER=postgres -TIMESCALE_PASSWORD=postgres - -# Data providers -DEFAULT_DATA_PROVIDER=alpaca -ALPACA_API_KEY=your_alpaca_key_here -ALPACA_API_SECRET=your_alpaca_secret_here -POLYGON_API_KEY=your_polygon_key_here - -# Risk parameters -RISK_MAX_DRAWDOWN=0.05 -RISK_MAX_POSITION_SIZE=0.1 -RISK_MAX_LEVERAGE=1.5 -RISK_STOP_LOSS_DEFAULT=0.02 -RISK_TAKE_PROFIT_DEFAULT=0.05 - -# Market Data Gateway -SERVICE_PORT=4000 -WEBSOCKET_ENABLED=true -WEBSOCKET_PATH=/ws/market-data -WEBSOCKET_HEARTBEAT_INTERVAL=30000 -THROTTLING_MAX_REQUESTS=300 -THROTTLING_MAX_CONNECTIONS=5 -CACHING_ENABLED=true -CACHING_TTL_SECONDS=60 - -# Risk Guardian -RISK_CHECKS_PRE_TRADE=true -RISK_CHECKS_PORTFOLIO=true -RISK_CHECKS_LEVERAGE=true -RISK_CHECKS_CONCENTRATION=true -ALERTING_ENABLED=true -ALERTING_CRITICAL_THRESHOLD=0.8 -ALERTING_WARNING_THRESHOLD=0.6 -WATCHDOG_ENABLED=true -WATCHDOG_CHECK_INTERVAL=60 +# HTTP Proxy (optional) +STOCKBOT_HTTP_PROXY_ENABLED=false +STOCKBOT_HTTP_PROXY_URL=http://proxy.example.com:8080 +STOCKBOT_HTTP_PROXY_AUTH_USERNAME=username +STOCKBOT_HTTP_PROXY_AUTH_PASSWORD=password \ No newline at end of file diff --git a/libs/config/.env.production.example b/libs/config/.env.production.example deleted file mode 100644 index 82e3435..0000000 --- a/libs/config/.env.production.example +++ /dev/null @@ -1,28 +0,0 @@ -# Production environment variables for Stock Bot - -# Environment -NODE_ENV=production - -# Logging -LOG_LEVEL=info - -# Database configuration (use environment-specific values) -DRAGONFLY_HOST=dragonfly.production -DRAGONFLY_PORT=6379 -DRAGONFLY_MAX_RETRIES_PER_REQUEST=5 - -TIMESCALE_HOST=timescale.production -TIMESCALE_PORT=5432 -TIMESCALE_DB=stockbot_prod - -# Risk parameters (more conservative for production) -RISK_MAX_DRAWDOWN=0.03 -RISK_MAX_POSITION_SIZE=0.05 -RISK_MAX_LEVERAGE=1.0 - -# Service settings -WEBSOCKET_HEARTBEAT_INTERVAL=15000 -THROTTLING_MAX_REQUESTS=500 -THROTTLING_MAX_CONNECTIONS=20 -CACHING_ENABLED=true -CACHING_TTL_SECONDS=30 diff --git a/libs/config/README.md b/libs/config/README.md index c39802b..2683ed2 100644 --- a/libs/config/README.md +++ b/libs/config/README.md @@ -1,103 +1,243 @@ -# @stock-bot/config - -A configuration management library for the Stock Bot trading platform. - -## Overview - -This library provides a centralized way to manage configurations across all Stock Bot microservices and components. It includes: - -- Environment-based configuration loading -- Strong TypeScript typing and validation using Zod -- Default configurations for services -- Environment variable parsing helpers -- Service-specific configuration modules - -## Usage - -### Basic Usage - -```typescript -import { databaseConfig, dataProviderConfigs, riskConfig } from '@stock-bot/config'; - -// Access database configuration -const dragonflyHost = databaseConfig.dragonfly.host; - -// Access data provider configuration -const alpacaApiKey = dataProviderConfigs.providers.find(p => p.name === 'alpaca')?.apiKey; - -// Access risk configuration -const maxPositionSize = riskConfig.maxPositionSize; -``` - -### Service-Specific Configuration - -```typescript -import { marketDataGatewayConfig, riskGuardianConfig } from '@stock-bot/config'; - -// Access Market Data Gateway configuration -const websocketPath = marketDataGatewayConfig.websocket.path; - -// Access Risk Guardian configuration -const preTradeValidation = riskGuardianConfig.riskChecks.preTradeValidation; -``` - -### Environment Variables - -The library automatically loads environment variables from `.env` files. You can create environment-specific files: - -- `.env` - Base environment variables -- `.env.development` - Development-specific variables -- `.env.production` - Production-specific variables -- `.env.local` - Local overrides (not to be committed to git) - -## Configuration Modules - -### Core Configuration - -- `Environment` - Enum for different environments -- `loadEnvVariables()` - Load environment variables from .env files -- `getEnvironment()` - Get the current environment -- `validateConfig()` - Validate configuration with Zod schema - -### Database Configuration - -- `databaseConfig` - Database connection settings (Dragonfly, QuestDB, MongoDB, PostgreSQL) - -### Data Provider Configuration - -- `dataProviderConfigs` - Settings for market data providers - -### Risk Configuration - -- `riskConfig` - Risk management parameters (max drawdown, position size, etc.) - -### Service-Specific Configuration - -- `marketDataGatewayConfig` - Configs for the Market Data Gateway service -- `riskGuardianConfig` - Configs for the Risk Guardian service - -## Extending - -To add a new service configuration: - -1. Create a new file in `src/services/` -2. Define a Zod schema for validation -3. Create loading and default configuration functions -4. Export from `src/services/index.ts` -5. The new configuration will be automatically available from the main package - -## Development - -```bash -# Install dependencies -bun install - -# Run tests -bun test - -# Type check -bun run type-check - -# Lint -bun run lint -``` +# @stock-bot/config + +A robust, type-safe configuration library for the Stock Bot application. Built with Zod for validation and supports multiple configuration sources with proper precedence. + +## Features + +- **Type-safe configuration** with Zod schemas +- **Multiple configuration sources**: JSON files and environment variables +- **Environment-specific overrides** (development, test, production) +- **Dynamic provider configurations** +- **No circular dependencies** - designed to be used by all other libraries +- **Clear error messages** with validation +- **Runtime configuration updates** (useful for testing) +- **Singleton pattern** for global configuration access + +## Installation + +```bash +bun add @stock-bot/config +``` + +## Usage + +### Basic Usage + +```typescript +import { initializeConfig, getConfig } from '@stock-bot/config'; + +// Initialize configuration (call once at app startup) +await initializeConfig(); + +// Get configuration +const config = getConfig(); +console.log(config.database.postgres.host); + +// Use convenience functions +import { getDatabaseConfig, isProduction } from '@stock-bot/config'; + +const dbConfig = getDatabaseConfig(); +if (isProduction()) { + // Production-specific logic +} +``` + +### Custom Configuration + +```typescript +import { ConfigManager } from '@stock-bot/config'; +import { z } from 'zod'; + +// Define your schema +const myConfigSchema = z.object({ + app: z.object({ + name: z.string(), + version: z.string(), + }), + features: z.object({ + enableBeta: z.boolean().default(false), + }), +}); + +// Create config manager +const configManager = new ConfigManager({ + configPath: './my-config', +}); + +// Initialize with schema +const config = await configManager.initialize(myConfigSchema); +``` + +### Provider-Specific Configuration + +```typescript +import { getProviderConfig } from '@stock-bot/config'; + +// Get provider configuration +const eodConfig = getProviderConfig('eod'); +console.log(eodConfig.apiKey); + +// Check if provider is enabled +if (eodConfig.enabled) { + // Use EOD provider +} +``` + +### Environment Variables + +Environment variables are loaded with the `STOCKBOT_` prefix and follow a naming convention: + +```bash +# Database configuration +STOCKBOT_DATABASE_POSTGRES_HOST=localhost +STOCKBOT_DATABASE_POSTGRES_PORT=5432 + +# Provider configuration +STOCKBOT_PROVIDERS_EOD_API_KEY=your_api_key +STOCKBOT_PROVIDERS_EOD_ENABLED=true + +# Service configuration +STOCKBOT_SERVICE_PORT=3000 +STOCKBOT_LOGGING_LEVEL=debug +``` + +### Configuration Precedence + +Configuration is loaded in the following order (later sources override earlier ones): + +1. `config/default.json` - Base configuration +2. `config/{environment}.json` - Environment-specific overrides +3. Environment variables - Highest priority + +### Advanced Usage + +```typescript +import { getConfigManager } from '@stock-bot/config'; + +const manager = getConfigManager(); + +// Get specific value by path +const port = manager.getValue('service.port'); + +// Check if configuration exists +if (manager.has('providers.ib')) { + // IB provider is configured +} + +// Update configuration at runtime (useful for testing) +manager.set({ + logging: { + level: 'debug' + } +}); + +// Create typed getter +const getQueueConfig = manager.createTypedGetter(queueConfigSchema); +const queueConfig = getQueueConfig(); +``` + +## Configuration Schema + +The library provides pre-defined schemas for common configurations: + +### Base Configuration +- `environment` - Current environment (development/test/production) +- `name` - Application name +- `version` - Application version +- `debug` - Debug mode flag + +### Service Configuration +- `name` - Service name +- `port` - Service port +- `host` - Service host +- `healthCheckPath` - Health check endpoint +- `cors` - CORS configuration + +### Database Configuration +- `postgres` - PostgreSQL configuration +- `questdb` - QuestDB configuration +- `mongodb` - MongoDB configuration +- `dragonfly` - Dragonfly/Redis configuration + +### Provider Configuration +- `eod` - EOD Historical Data provider +- `ib` - Interactive Brokers provider +- `qm` - QuoteMedia provider +- `yahoo` - Yahoo Finance provider + +## Testing + +```typescript +import { resetConfig, initializeConfig } from '@stock-bot/config'; + +beforeEach(() => { + resetConfig(); +}); + +test('custom config', async () => { + process.env.NODE_ENV = 'test'; + process.env.STOCKBOT_SERVICE_PORT = '4000'; + + await initializeConfig(); + const config = getConfig(); + + expect(config.service.port).toBe(4000); +}); +``` + +## Custom Loaders + +You can create custom configuration loaders: + +```typescript +import { ConfigLoader } from '@stock-bot/config'; + +class ApiConfigLoader implements ConfigLoader { + readonly priority = 75; // Between file (50) and env (100) + + async load(): Promise> { + // Fetch configuration from API + const response = await fetch('https://api.example.com/config'); + return response.json(); + } +} + +// Use custom loader +const configManager = new ConfigManager({ + loaders: [ + new FileLoader('./config', 'production'), + new ApiConfigLoader(), + new EnvLoader('STOCKBOT_'), + ], +}); +``` + +## Error Handling + +The library provides specific error types: + +```typescript +import { ConfigError, ConfigValidationError } from '@stock-bot/config'; + +try { + await initializeConfig(); +} catch (error) { + if (error instanceof ConfigValidationError) { + console.error('Validation failed:', error.errors); + } else if (error instanceof ConfigError) { + console.error('Configuration error:', error.message); + } +} +``` + +## Best Practices + +1. **Initialize once**: Call `initializeConfig()` once at application startup +2. **Use schemas**: Always define and validate configurations with Zod schemas +3. **Environment variables**: Use the `STOCKBOT_` prefix for all env vars +4. **Type safety**: Leverage TypeScript types from the schemas +5. **Testing**: Reset configuration between tests with `resetConfig()` + +## License + +MIT \ No newline at end of file diff --git a/libs/config/USAGE.md b/libs/config/USAGE.md deleted file mode 100644 index b3fc5cc..0000000 --- a/libs/config/USAGE.md +++ /dev/null @@ -1,131 +0,0 @@ -# Stock Bot Configuration Library Usage Guide - -This guide shows how to use the Zod-based configuration system in the Stock Bot platform. - -## Quick Start - -```typescript -import { databaseConfig, loggingConfig, riskConfig, dataProvidersConfig } from '@stock-bot/config'; - -// Access individual values -console.log(`Database: ${databaseConfig.POSTGRES_HOST}:${databaseConfig.POSTGRES_PORT}`); -console.log(`Log level: ${loggingConfig.LOG_LEVEL}`); -console.log(`Max position size: ${riskConfig.RISK_MAX_POSITION_SIZE}`); -``` - -## Environment Variables - -All configuration is driven by environment variables. You can set them in: -- `.env` files -- System environment variables -- Docker environment variables - -### Database Configuration -```bash -DB_HOST=localhost -DB_PORT=5432 -DB_NAME=stockbot -DB_USER=stockbot -DB_PASSWORD=your_password -DB_SSL=false -DB_POOL_MAX=10 -``` - -### Logging Configuration -```bash -LOG_LEVEL=info -LOG_CONSOLE=true -LOKI_HOST=localhost -LOKI_PORT=3100 -LOKI_LABELS=service=market-data-gateway,version=1.0.0 -``` - -### Risk Management Configuration -```bash -RISK_MAX_POSITION_SIZE=0.1 -RISK_DEFAULT_STOP_LOSS=0.05 -RISK_DEFAULT_TAKE_PROFIT=0.15 -RISK_CIRCUIT_BREAKER_ENABLED=true -``` - -### Data Provider Configuration -```bash -DEFAULT_DATA_PROVIDER=alpaca -ALPACA_API_KEY=your_api_key -ALPACA_API_SECRET=your_api_secret -ALPACA_ENABLED=true -POLYGON_ENABLED=false -``` - -## Advanced Usage - -### Type Safety -All configurations are fully typed: - -```typescript -import type { DatabaseConfig, LoggingConfig, RiskConfig } from '@stock-bot/config'; - -function setupDatabase(config: DatabaseConfig) { - // TypeScript knows all the available properties - return { - host: config.POSTGRES_HOST, - port: config.POSTGRES_PORT, // number - ssl: config.POSTGRES_SSL, // boolean - }; -} -``` - -### Environment Detection -```typescript -import { getEnvironment, Environment } from '@stock-bot/config'; - -const env = getEnvironment(); -if (env === Environment.Production) { - // Production-specific logic -} -``` - -### Data Provider Helpers -```typescript -import { getProviderConfig, getEnabledProviders, getDefaultProvider } from '@stock-bot/config'; - -// Get specific provider -const alpaca = getProviderConfig('alpaca'); - -// Get all enabled providers -const providers = getEnabledProviders(); - -// Get default provider -const defaultProvider = getDefaultProvider(); -``` - -## Configuration Files - -The library consists of these modules: - -- **core.ts** - Core utilities and environment detection -- **database.ts** - Database connection settings -- **logging.ts** - Logging and Loki configuration -- **risk.ts** - Risk management parameters -- **data-providers.ts** - Data provider settings - -## Benefits of This Approach - -1. **Zero Configuration Schema** - No complex schema definitions needed -2. **Automatic Type Inference** - TypeScript types are generated automatically -3. **Environment Variable Validation** - Invalid values are caught at startup -4. **Great Developer Experience** - IntelliSense works perfectly -5. **Production Ready** - Used by many large-scale applications - -## Migration from Previous System - -If you're migrating from the old Valibot-based system: - -```typescript -// Old way -const config = createConfigLoader('database', databaseSchema, defaultConfig)(); - -// New way -import { databaseConfig } from '@stock-bot/config'; -// That's it! No schema needed, no validation needed, no complex setup. -``` diff --git a/libs/config/bunfig.toml b/libs/config/bunfig.toml deleted file mode 100644 index 5c300fc..0000000 --- a/libs/config/bunfig.toml +++ /dev/null @@ -1,15 +0,0 @@ -[test] -# Configure path mapping for tests -preload = ["./test/setup.ts"] - -# Test configuration -timeout = 5000 - -# Set test environment -env = { NODE_ENV = "test" } - -[bun] -# Enable TypeScript paths resolution -paths = { - "@/*" = ["./src/*"] -} diff --git a/libs/config-new/config/default.json b/libs/config/config/default.json similarity index 100% rename from libs/config-new/config/default.json rename to libs/config/config/default.json diff --git a/libs/config-new/config/development.json b/libs/config/config/development.json similarity index 100% rename from libs/config-new/config/development.json rename to libs/config/config/development.json diff --git a/libs/config-new/config/production.json b/libs/config/config/production.json similarity index 100% rename from libs/config-new/config/production.json rename to libs/config/config/production.json diff --git a/libs/config-new/config/test.json b/libs/config/config/test.json similarity index 100% rename from libs/config-new/config/test.json rename to libs/config/config/test.json diff --git a/libs/config/package.json b/libs/config/package.json index 29f4fc6..96102b2 100644 --- a/libs/config/package.json +++ b/libs/config/package.json @@ -1,44 +1,36 @@ { "name": "@stock-bot/config", "version": "1.0.0", - "description": "Configuration management library for Stock Bot platform", - "main": "dist/index.js", - "types": "dist/index.d.ts", "type": "module", - "scripts": { - "build": "tsc", - "test": "bun test", - "lint": "eslint src/**/*.ts", - "type-check": "tsc --noEmit", - "clean": "rimraf dist" - }, - "dependencies": { - "dotenv": "^16.5.0", - "yup": "^1.6.1" - }, - "devDependencies": { - "@types/node": "^20.11.0", - "typescript": "^5.3.0", - "eslint": "^8.56.0", - "@typescript-eslint/eslint-plugin": "^6.19.0", - "@typescript-eslint/parser": "^6.19.0", - "bun-types": "^1.2.15" - }, - "keywords": [ - "configuration", - "settings", - "env", - "stock-bot" - ], "exports": { ".": { "import": "./dist/index.js", - "require": "./dist/index.js", "types": "./dist/index.d.ts" } }, - "files": [ - "dist", - "README.md" - ] + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "test": "bun test", + "clean": "rm -rf dist", + "cli": "bun run src/cli.ts", + "validate": "bun run src/cli.ts --validate", + "check": "bun run src/cli.ts --check" + }, + "bin": { + "config-cli": "./dist/cli.js" + }, + "dependencies": { + "zod": "^3.22.4" + }, + "devDependencies": { + "@types/bun": "^1.0.0", + "@types/node": "^20.10.5", + "typescript": "^5.3.3" + }, + "peerDependencies": { + "bun": "^1.0.0" + } } diff --git a/libs/config/setup.bat b/libs/config/setup.bat deleted file mode 100644 index a2cc8b7..0000000 --- a/libs/config/setup.bat +++ /dev/null @@ -1,24 +0,0 @@ -@echo off -echo Building @stock-bot/config library... - -cd /d g:\repos\stock-bot -echo Installing dependencies... -bun install - -echo Running type check... -cd /d g:\repos\stock-bot\libs\config -bun run type-check - -echo Running tests... -bun test - -echo Setting up example configuration... -copy .env.example .env - -echo Running example to display configuration... -bun run src/example.ts - -echo. -echo Configuration library setup complete! -echo. -echo You can now import @stock-bot/config in your services. diff --git a/libs/config/src/admin-interfaces.ts b/libs/config/src/admin-interfaces.ts deleted file mode 100644 index c0801ab..0000000 --- a/libs/config/src/admin-interfaces.ts +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Admin interfaces configuration using Yup - * PgAdmin, Mongo Express, Redis Insight for database management - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, port, bool, strWithChoices } = envValidators; - -/** - * PgAdmin configuration with validation and defaults - */ -export const pgAdminConfig = cleanEnv(process.env, { - // PgAdmin Server - PGADMIN_HOST: str('localhost', 'PgAdmin host'), - PGADMIN_PORT: port(8080, 'PgAdmin port'), - - // Authentication - PGADMIN_DEFAULT_EMAIL: str('admin@tradingbot.local', 'PgAdmin default admin email'), - PGADMIN_DEFAULT_PASSWORD: str('admin123', 'PgAdmin default admin password'), - - // Configuration - PGADMIN_SERVER_MODE: bool(false, 'Enable server mode (multi-user)'), - PGADMIN_DISABLE_POSTFIX: bool(true, 'Disable postfix for email'), - PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: bool(true, 'Enhanced cookie protection'), - - // Security - PGADMIN_MASTER_PASSWORD_REQUIRED: bool(false, 'Require master password'), - PGADMIN_SESSION_TIMEOUT: str('60', 'Session timeout in minutes'), -}); - -/** - * Mongo Express configuration with validation and defaults - */ -export const mongoExpressConfig = cleanEnv(process.env, { - // Mongo Express Server - MONGO_EXPRESS_HOST: str('localhost', 'Mongo Express host'), - MONGO_EXPRESS_PORT: port(8081, 'Mongo Express port'), - - // MongoDB Connection - MONGO_EXPRESS_MONGODB_SERVER: str('mongodb', 'MongoDB server name/host'), - MONGO_EXPRESS_MONGODB_PORT: port(27017, 'MongoDB port'), - MONGO_EXPRESS_MONGODB_ADMINUSERNAME: str('trading_admin', 'MongoDB admin username'), - MONGO_EXPRESS_MONGODB_ADMINPASSWORD: str('', 'MongoDB admin password'), - - // Basic Authentication for Mongo Express - MONGO_EXPRESS_BASICAUTH_USERNAME: str('admin', 'Basic auth username for Mongo Express'), - MONGO_EXPRESS_BASICAUTH_PASSWORD: str('admin123', 'Basic auth password for Mongo Express'), - - // Configuration - MONGO_EXPRESS_ENABLE_ADMIN: bool(true, 'Enable admin features'), - MONGO_EXPRESS_OPTIONS_EDITOR_THEME: str('rubyblue', 'Editor theme (rubyblue, 3024-night, etc.)'), - MONGO_EXPRESS_REQUEST_SIZE: str('100kb', 'Maximum request size'), -}); - -/** - * Redis Insight configuration with validation and defaults - */ -export const redisInsightConfig = cleanEnv(process.env, { - // Redis Insight Server - REDIS_INSIGHT_HOST: str('localhost', 'Redis Insight host'), - REDIS_INSIGHT_PORT: port(8001, 'Redis Insight port'), - - // Redis Connection Settings - REDIS_INSIGHT_REDIS_HOSTS: str( - 'local:dragonfly:6379', - 'Redis hosts in format name:host:port,name:host:port' - ), - - // Configuration - REDIS_INSIGHT_LOG_LEVEL: strWithChoices( - ['error', 'warn', 'info', 'verbose', 'debug'], - 'info', - 'Redis Insight log level' - ), - REDIS_INSIGHT_DISABLE_ANALYTICS: bool(true, 'Disable analytics collection'), - REDIS_INSIGHT_BUILD_TYPE: str('DOCKER', 'Build type identifier'), -}); - -// Export typed configuration objects -export type PgAdminConfig = typeof pgAdminConfig; -export type MongoExpressConfig = typeof mongoExpressConfig; -export type RedisInsightConfig = typeof redisInsightConfig; - -// Export individual config values for convenience -export const { - PGADMIN_HOST, - PGADMIN_PORT, - PGADMIN_DEFAULT_EMAIL, - PGADMIN_DEFAULT_PASSWORD, - PGADMIN_SERVER_MODE, - PGADMIN_DISABLE_POSTFIX, - PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION, - PGADMIN_MASTER_PASSWORD_REQUIRED, - PGADMIN_SESSION_TIMEOUT, -} = pgAdminConfig; - -export const { - MONGO_EXPRESS_HOST, - MONGO_EXPRESS_PORT, - MONGO_EXPRESS_MONGODB_SERVER, - MONGO_EXPRESS_MONGODB_PORT, - MONGO_EXPRESS_MONGODB_ADMINUSERNAME, - MONGO_EXPRESS_MONGODB_ADMINPASSWORD, - MONGO_EXPRESS_BASICAUTH_USERNAME, - MONGO_EXPRESS_BASICAUTH_PASSWORD, - MONGO_EXPRESS_ENABLE_ADMIN, - MONGO_EXPRESS_OPTIONS_EDITOR_THEME, - MONGO_EXPRESS_REQUEST_SIZE, -} = mongoExpressConfig; - -export const { - REDIS_INSIGHT_HOST, - REDIS_INSIGHT_PORT, - REDIS_INSIGHT_REDIS_HOSTS, - REDIS_INSIGHT_LOG_LEVEL, - REDIS_INSIGHT_DISABLE_ANALYTICS, - REDIS_INSIGHT_BUILD_TYPE, -} = redisInsightConfig; diff --git a/libs/config-new/src/cli.ts b/libs/config/src/cli.ts similarity index 100% rename from libs/config-new/src/cli.ts rename to libs/config/src/cli.ts diff --git a/libs/config-new/src/config-manager.ts b/libs/config/src/config-manager.ts similarity index 100% rename from libs/config-new/src/config-manager.ts rename to libs/config/src/config-manager.ts diff --git a/libs/config/src/core.ts b/libs/config/src/core.ts deleted file mode 100644 index ea8eaf3..0000000 --- a/libs/config/src/core.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Core configuration module for the Stock Bot platform using Yup - */ -import path from 'node:path'; -import { config as dotenvConfig } from 'dotenv'; - -/** - * Represents an error related to configuration validation - */ -export class ConfigurationError extends Error { - constructor(message: string) { - super(message); - this.name = 'ConfigurationError'; - } -} - -/** - * Environment types - */ -export enum Environment { - Development = 'development', - Testing = 'testing', - Staging = 'staging', - Production = 'production', -} - -/** - * Loads environment variables from .env files based on the current environment - */ -export function loadEnvVariables(envOverride?: string): void { - const env = envOverride || process.env.NODE_ENV || 'development'; - console.log(`Current environment: ${env}`); - // Order of loading: - // 1. .env (base environment variables) - // 2. .env.{environment} (environment-specific variables) - // 3. .env.local (local overrides, not to be committed) - - const envFiles = ['.env', `.env.${env}`, '.env.local']; - - for (const file of envFiles) { - dotenvConfig({ path: path.resolve(process.cwd(), file) }); - } -} - -/** - * Gets the current environment from process.env.NODE_ENV - */ -export function getEnvironment(): Environment { - const env = process.env.NODE_ENV?.toLowerCase() || 'development'; - switch (env) { - case 'development': - return Environment.Development; - case 'testing': - case 'test': // Handle both 'test' and 'testing' for compatibility - return Environment.Testing; - case 'staging': - return Environment.Staging; - case 'production': - return Environment.Production; - default: - return Environment.Development; - } -} diff --git a/libs/config/src/data-providers.ts b/libs/config/src/data-providers.ts deleted file mode 100644 index 033fec8..0000000 --- a/libs/config/src/data-providers.ts +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Data provider configurations using Yup - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, num, bool, strWithChoices } = envValidators; - -export interface ProviderConfig { - name: string; - type: 'rest' | 'websocket'; - enabled: boolean; - baseUrl?: string; - apiKey?: string; - apiSecret?: string; - rateLimits?: { - maxRequestsPerMinute?: number; - maxRequestsPerSecond?: number; - maxRequestsPerHour?: number; - }; -} -/** - * Data providers configuration with validation and defaults - */ -export const dataProvidersConfig = cleanEnv(process.env, { - // Default Provider - DEFAULT_DATA_PROVIDER: strWithChoices( - ['alpaca', 'polygon', 'yahoo', 'iex'], - 'alpaca', - 'Default data provider' - ), - - // Alpaca Configuration - ALPACA_API_KEY: str('', 'Alpaca API key'), - ALPACA_API_SECRET: str('', 'Alpaca API secret'), - ALPACA_BASE_URL: str('https://data.alpaca.markets/v1beta1', 'Alpaca base URL'), - ALPACA_RATE_LIMIT: num(200, 'Alpaca rate limit per minute'), - ALPACA_ENABLED: bool(true, 'Enable Alpaca provider'), - - // Polygon Configuration - POLYGON_API_KEY: str('', 'Polygon API key'), - POLYGON_BASE_URL: str('https://api.polygon.io', 'Polygon base URL'), - POLYGON_RATE_LIMIT: num(5, 'Polygon rate limit per minute'), - POLYGON_ENABLED: bool(false, 'Enable Polygon provider'), - - // Yahoo Finance Configuration - YAHOO_BASE_URL: str('https://query1.finance.yahoo.com', 'Yahoo Finance base URL'), - YAHOO_RATE_LIMIT: num(2000, 'Yahoo Finance rate limit per hour'), - YAHOO_ENABLED: bool(true, 'Enable Yahoo Finance provider'), - - // IEX Cloud Configuration - IEX_API_KEY: str('', 'IEX Cloud API key'), - IEX_BASE_URL: str('https://cloud.iexapis.com/stable', 'IEX Cloud base URL'), - IEX_RATE_LIMIT: num(100, 'IEX Cloud rate limit per second'), - IEX_ENABLED: bool(false, 'Enable IEX Cloud provider'), - - // Connection Settings - DATA_PROVIDER_TIMEOUT: num(30000, 'Request timeout in milliseconds'), - DATA_PROVIDER_RETRIES: num(3, 'Number of retry attempts'), - DATA_PROVIDER_RETRY_DELAY: num(1000, 'Retry delay in milliseconds'), - - // Cache Settings - DATA_CACHE_ENABLED: bool(true, 'Enable data caching'), - DATA_CACHE_TTL: num(300000, 'Cache TTL in milliseconds'), - DATA_CACHE_MAX_SIZE: num(1000, 'Maximum cache entries'), -}); - -/** - * Helper function to get provider-specific configuration - */ -export function getProviderConfig(providerName: string) { - // make a interface for the provider config - - const name = providerName.toUpperCase(); - - switch (name) { - case 'ALPACA': - return { - name: 'alpaca', - type: 'rest' as const, - enabled: dataProvidersConfig.ALPACA_ENABLED, - baseUrl: dataProvidersConfig.ALPACA_BASE_URL, - apiKey: dataProvidersConfig.ALPACA_API_KEY, - apiSecret: dataProvidersConfig.ALPACA_API_SECRET, - rateLimits: { - maxRequestsPerMinute: dataProvidersConfig.ALPACA_RATE_LIMIT, - }, - }; - - case 'POLYGON': - return { - name: 'polygon', - type: 'rest' as const, - enabled: dataProvidersConfig.POLYGON_ENABLED, - baseUrl: dataProvidersConfig.POLYGON_BASE_URL, - apiKey: dataProvidersConfig.POLYGON_API_KEY, - rateLimits: { - maxRequestsPerMinute: dataProvidersConfig.POLYGON_RATE_LIMIT, - }, - }; - - case 'YAHOO': - return { - name: 'yahoo', - type: 'rest' as const, - enabled: dataProvidersConfig.YAHOO_ENABLED, - baseUrl: dataProvidersConfig.YAHOO_BASE_URL, - rateLimits: { - maxRequestsPerHour: dataProvidersConfig.YAHOO_RATE_LIMIT, - }, - }; - - case 'IEX': - return { - name: 'iex', - type: 'rest' as const, - enabled: dataProvidersConfig.IEX_ENABLED, - baseUrl: dataProvidersConfig.IEX_BASE_URL, - apiKey: dataProvidersConfig.IEX_API_KEY, - rateLimits: { - maxRequestsPerSecond: dataProvidersConfig.IEX_RATE_LIMIT, - }, - }; - - default: - throw new Error(`Unknown provider: ${providerName}`); - } -} - -/** - * Get all enabled providers - */ -export function getEnabledProviders() { - const providers = ['alpaca', 'polygon', 'yahoo', 'iex']; - return providers.map(provider => getProviderConfig(provider)).filter(config => config.enabled); -} - -/** - * Get the default provider configuration - */ -export function getDefaultProvider() { - return getProviderConfig(dataProvidersConfig.DEFAULT_DATA_PROVIDER); -} - -// Export typed configuration object -export type DataProvidersConfig = typeof dataProvidersConfig; -export class DataProviders { - static getProviderConfig(providerName: string): ProviderConfig { - return getProviderConfig(providerName); - } - - static getEnabledProviders(): ProviderConfig[] { - return getEnabledProviders(); - } - - static getDefaultProvider(): ProviderConfig { - return getDefaultProvider(); - } -} - -// Export individual config values for convenience -export const { - DEFAULT_DATA_PROVIDER, - ALPACA_API_KEY, - ALPACA_API_SECRET, - ALPACA_BASE_URL, - ALPACA_RATE_LIMIT, - ALPACA_ENABLED, - POLYGON_API_KEY, - POLYGON_BASE_URL, - POLYGON_RATE_LIMIT, - POLYGON_ENABLED, - YAHOO_BASE_URL, - YAHOO_RATE_LIMIT, - YAHOO_ENABLED, - IEX_API_KEY, - IEX_BASE_URL, - IEX_RATE_LIMIT, - IEX_ENABLED, - DATA_PROVIDER_TIMEOUT, - DATA_PROVIDER_RETRIES, - DATA_PROVIDER_RETRY_DELAY, - DATA_CACHE_ENABLED, - DATA_CACHE_TTL, - DATA_CACHE_MAX_SIZE, -} = dataProvidersConfig; diff --git a/libs/config/src/database.ts b/libs/config/src/database.ts deleted file mode 100644 index 36ca11c..0000000 --- a/libs/config/src/database.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Database configuration using Yup - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, port, num, bool } = envValidators; - -/** - * Database configuration with validation and defaults - */ -export const databaseConfig = cleanEnv(process.env, { - // PostgreSQL Configuration - DB_HOST: str('localhost', 'Database host'), - DB_PORT: port(5432, 'Database port'), - DB_NAME: str('stockbot', 'Database name'), - DB_USER: str('stockbot', 'Database user'), - DB_PASSWORD: str('', 'Database password'), - - // Connection Pool Settings - DB_POOL_MIN: num(2, 'Minimum pool connections'), - DB_POOL_MAX: num(10, 'Maximum pool connections'), - DB_POOL_IDLE_TIMEOUT: num(30000, 'Pool idle timeout in ms'), - - // SSL Configuration - DB_SSL: bool(false, 'Enable SSL for database connection'), - DB_SSL_REJECT_UNAUTHORIZED: bool(true, 'Reject unauthorized SSL certificates'), - - // Additional Settings - DB_QUERY_TIMEOUT: num(30000, 'Query timeout in ms'), - DB_CONNECTION_TIMEOUT: num(5000, 'Connection timeout in ms'), - DB_STATEMENT_TIMEOUT: num(30000, 'Statement timeout in ms'), - DB_LOCK_TIMEOUT: num(10000, 'Lock timeout in ms'), - DB_IDLE_IN_TRANSACTION_SESSION_TIMEOUT: num(60000, 'Idle in transaction timeout in ms'), -}); - -// Export typed configuration object -export type DatabaseConfig = typeof databaseConfig; - -// Export individual config values for convenience -export const { - DB_HOST, - DB_PORT, - DB_NAME, - DB_USER, - DB_PASSWORD, - DB_POOL_MIN, - DB_POOL_MAX, - DB_POOL_IDLE_TIMEOUT, - DB_SSL, - DB_SSL_REJECT_UNAUTHORIZED, - DB_QUERY_TIMEOUT, - DB_CONNECTION_TIMEOUT, - DB_STATEMENT_TIMEOUT, - DB_LOCK_TIMEOUT, - DB_IDLE_IN_TRANSACTION_SESSION_TIMEOUT, -} = databaseConfig; diff --git a/libs/config/src/dragonfly.ts b/libs/config/src/dragonfly.ts deleted file mode 100644 index 0850d32..0000000 --- a/libs/config/src/dragonfly.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Dragonfly (Redis replacement) configuration using Yup - * High-performance caching and event streaming - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, port, num, bool } = envValidators; - -/** - * Dragonfly configuration with validation and defaults - */ -export const dragonflyConfig = cleanEnv(process.env, { - // Dragonfly Connection - DRAGONFLY_HOST: str('localhost', 'Dragonfly host'), - DRAGONFLY_PORT: port(6379, 'Dragonfly port'), - DRAGONFLY_PASSWORD: str('', 'Dragonfly password (if auth enabled)'), - DRAGONFLY_USERNAME: str('', 'Dragonfly username (if ACL enabled)'), - - // Database Selection - DRAGONFLY_DATABASE: num(0, 'Dragonfly database number (0-15)'), - - // Connection Pool Settings - DRAGONFLY_MAX_RETRIES: num(3, 'Maximum retry attempts'), - DRAGONFLY_RETRY_DELAY: num(50, 'Retry delay in ms'), - DRAGONFLY_CONNECT_TIMEOUT: num(10000, 'Connection timeout in ms'), - DRAGONFLY_COMMAND_TIMEOUT: num(5000, 'Command timeout in ms'), - - // Pool Configuration - DRAGONFLY_POOL_SIZE: num(10, 'Connection pool size'), - DRAGONFLY_POOL_MIN: num(1, 'Minimum pool connections'), - DRAGONFLY_POOL_MAX: num(20, 'Maximum pool connections'), - - // TLS Settings - DRAGONFLY_TLS: bool(false, 'Enable TLS for Dragonfly connection'), - DRAGONFLY_TLS_CERT_FILE: str('', 'Path to TLS certificate file'), - DRAGONFLY_TLS_KEY_FILE: str('', 'Path to TLS key file'), - DRAGONFLY_TLS_CA_FILE: str('', 'Path to TLS CA certificate file'), - DRAGONFLY_TLS_SKIP_VERIFY: bool(false, 'Skip TLS certificate verification'), - - // Performance Settings - DRAGONFLY_ENABLE_KEEPALIVE: bool(true, 'Enable TCP keepalive'), - DRAGONFLY_KEEPALIVE_INTERVAL: num(60, 'Keepalive interval in seconds'), - - // Clustering (if using cluster mode) - DRAGONFLY_CLUSTER_MODE: bool(false, 'Enable cluster mode'), - DRAGONFLY_CLUSTER_NODES: str('', 'Comma-separated list of cluster nodes (host:port)'), - - // Memory and Cache Settings - DRAGONFLY_MAX_MEMORY: str('2gb', 'Maximum memory usage'), - DRAGONFLY_CACHE_MODE: bool(true, 'Enable cache mode'), -}); - -// Export typed configuration object -export type DragonflyConfig = typeof dragonflyConfig; - -// Export individual config values for convenience -export const { - DRAGONFLY_HOST, - DRAGONFLY_PORT, - DRAGONFLY_PASSWORD, - DRAGONFLY_USERNAME, - DRAGONFLY_DATABASE, - DRAGONFLY_MAX_RETRIES, - DRAGONFLY_RETRY_DELAY, - DRAGONFLY_CONNECT_TIMEOUT, - DRAGONFLY_COMMAND_TIMEOUT, - DRAGONFLY_POOL_SIZE, - DRAGONFLY_POOL_MIN, - DRAGONFLY_POOL_MAX, - DRAGONFLY_TLS, - DRAGONFLY_TLS_CERT_FILE, - DRAGONFLY_TLS_KEY_FILE, - DRAGONFLY_TLS_CA_FILE, - DRAGONFLY_TLS_SKIP_VERIFY, - DRAGONFLY_ENABLE_KEEPALIVE, - DRAGONFLY_KEEPALIVE_INTERVAL, - DRAGONFLY_CLUSTER_MODE, - DRAGONFLY_CLUSTER_NODES, - DRAGONFLY_MAX_MEMORY, - DRAGONFLY_CACHE_MODE, -} = dragonflyConfig; diff --git a/libs/config/src/env-utils.ts b/libs/config/src/env-utils.ts deleted file mode 100644 index 55177e9..0000000 --- a/libs/config/src/env-utils.ts +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Environment validation utilities using Yup - */ -import { existsSync } from 'fs'; -import { join } from 'path'; -import { config } from 'dotenv'; -import * as yup from 'yup'; - -// Function to find and load environment variables -function loadEnvFiles() { - const cwd = process.cwd(); - const possiblePaths = [ - // Current working directory - join(cwd, '.env'), - join(cwd, '.env.local'), - // Root of the workspace (common pattern) - join(cwd, '../../.env'), - join(cwd, '../../../.env'), - // Config library directory - join(__dirname, '../.env'), - join(__dirname, '../../.env'), - join(__dirname, '../../../.env'), - ]; - - // Try to load each possible .env file - for (const envPath of possiblePaths) { - if (existsSync(envPath)) { - console.log(`šŸ“„ Loading environment from: ${envPath}`); - config({ path: envPath }); - break; // Use the first .env file found - } - } - - // Also try to load environment-specific files - const environment = process.env.NODE_ENV || 'development'; - const envSpecificPaths = [ - join(cwd, `.env.${environment}`), - join(cwd, `.env.${environment}.local`), - ]; - - for (const envPath of envSpecificPaths) { - if (existsSync(envPath)) { - console.log(`šŸ“„ Loading ${environment} environment from: ${envPath}`); - config({ path: envPath, override: false }); // Don't override existing vars - } - } -} - -// Load environment variables -loadEnvFiles(); - -/** - * Creates a Yup schema for environment variable validation - */ -export function createEnvSchema(shape: Record) { - return yup.object(shape); -} - -/** - * Validates environment variables against a Yup schema - */ -export function validateEnv(schema: yup.ObjectSchema, env = process.env): any { - try { - const result = schema.validateSync(env, { abortEarly: false }); - return result; - } catch (error) { - if (error instanceof yup.ValidationError) { - console.error('āŒ Invalid environment variables:'); - error.inner.forEach(err => { - console.error(` ${err.path}: ${err.message}`); - }); - } - throw new Error('Environment validation failed'); - } -} - -/** - * Manually load environment variables from a specific path - */ -export function loadEnv(path?: string) { - if (path) { - console.log(`šŸ“„ Manually loading environment from: ${path}`); - config({ path }); - } else { - loadEnvFiles(); - } -} - -/** - * Helper functions for common validation patterns - */ -export const envValidators = { - // String with default - str: (defaultValue?: string, description?: string) => yup.string().default(defaultValue || ''), - - // String with choices (enum) - strWithChoices: (choices: string[], defaultValue?: string, description?: string) => - yup - .string() - .oneOf(choices) - .default(defaultValue || choices[0]), - - // Required string - requiredStr: (description?: string) => yup.string().required('Required'), - - // Port number - port: (defaultValue?: number, description?: string) => - yup - .number() - .integer() - .min(1) - .max(65535) - .transform((val, originalVal) => { - if (typeof originalVal === 'string') { - return parseInt(originalVal, 10); - } - return val; - }) - .default(defaultValue || 3000), - - // Number with default - num: (defaultValue?: number, description?: string) => - yup - .number() - .transform((val, originalVal) => { - if (typeof originalVal === 'string') { - return parseFloat(originalVal); - } - return val; - }) - .default(defaultValue || 0), - - // Boolean with default - bool: (defaultValue?: boolean, description?: string) => - yup - .boolean() - .transform((val, originalVal) => { - if (typeof originalVal === 'string') { - return originalVal === 'true' || originalVal === '1'; - } - return val; - }) - .default(defaultValue || false), - - // URL validation - url: (defaultValue?: string, description?: string) => - yup - .string() - .url() - .default(defaultValue || 'http://localhost'), - - // Email validation - email: (description?: string) => yup.string().email(), -}; - -/** - * Legacy compatibility - creates a cleanEnv-like function - */ -export function cleanEnv( - env: Record, - validators: Record -): any { - const schema = createEnvSchema(validators); - return validateEnv(schema, env); -} diff --git a/libs/config-new/src/errors.ts b/libs/config/src/errors.ts similarity index 100% rename from libs/config-new/src/errors.ts rename to libs/config/src/errors.ts diff --git a/libs/config/src/index.ts b/libs/config/src/index.ts index fb82af4..41da483 100644 --- a/libs/config/src/index.ts +++ b/libs/config/src/index.ts @@ -1,20 +1,127 @@ -/** - * @stock-bot/config - * - * Configuration management library for Stock Bot platform using Yup - */ +// Export all schemas +export * from './schemas'; -// Re-export everything from all modules -export * from './env-utils'; -export * from './core'; -export * from './admin-interfaces'; -export * from './database'; -export * from './dragonfly'; -export * from './postgres'; -export * from './questdb'; -export * from './mongodb'; -export * from './logging'; -export * from './loki'; -export * from './monitoring'; -export * from './data-providers'; -export * from './risk'; +// Export types +export * from './types'; + +// Export errors +export * from './errors'; + +// Export loaders +export { EnvLoader } from './loaders/env.loader'; +export { FileLoader } from './loaders/file.loader'; + +// Export ConfigManager +export { ConfigManager } from './config-manager'; + +// Export utilities +export * from './utils/secrets'; +export * from './utils/validation'; + +// Import necessary types for singleton +import { ConfigManager } from './config-manager'; +import { AppConfig, appConfigSchema } from './schemas'; +import { FileLoader } from './loaders/file.loader'; +import { EnvLoader } from './loaders/env.loader'; + +// Create singleton instance +let configInstance: ConfigManager | null = null; + +/** + * Initialize the global configuration + */ +export async function initializeConfig( + configPath?: string +): Promise { + if (!configInstance) { + configInstance = new ConfigManager({ + configPath, + }); + } + return configInstance.initialize(appConfigSchema); +} + +/** + * Initialize configuration for a service in a monorepo + * Automatically loads configs from: + * 1. Root config directory (../../config) + * 2. Service-specific config directory (./config) + * 3. Environment variables + */ +export async function initializeServiceConfig(): Promise { + if (!configInstance) { + const environment = process.env.NODE_ENV || 'development'; + configInstance = new ConfigManager({ + loaders: [ + new FileLoader('../../config', environment), // Root config + new FileLoader('./config', environment), // Service config + new EnvLoader(''), // Environment variables + ] + }); + } + return configInstance.initialize(appConfigSchema); +} + +/** + * Get the current configuration + */ +export function getConfig(): AppConfig { + if (!configInstance) { + throw new Error('Configuration not initialized. Call initializeConfig() first.'); + } + return configInstance.get(); +} + +/** + * Get configuration manager instance + */ +export function getConfigManager(): ConfigManager { + if (!configInstance) { + throw new Error('Configuration not initialized. Call initializeConfig() first.'); + } + return configInstance; +} + +/** + * Reset configuration (useful for testing) + */ +export function resetConfig(): void { + if (configInstance) { + configInstance.reset(); + configInstance = null; + } +} + +// Export convenience functions for common configs +export function getDatabaseConfig() { + return getConfig().database; +} + +export function getServiceConfig() { + return getConfig().service; +} + +export function getLoggingConfig() { + return getConfig().logging; +} + +export function getProviderConfig(provider: string) { + const providers = getConfig().providers; + if (!providers || !(provider in providers)) { + throw new Error(`Provider configuration not found: ${provider}`); + } + return (providers as any)[provider]; +} + +// Export environment helpers +export function isDevelopment(): boolean { + return getConfig().environment === 'development'; +} + +export function isProduction(): boolean { + return getConfig().environment === 'production'; +} + +export function isTest(): boolean { + return getConfig().environment === 'test'; +} \ No newline at end of file diff --git a/libs/config-new/src/loaders/env.loader.ts b/libs/config/src/loaders/env.loader.ts similarity index 100% rename from libs/config-new/src/loaders/env.loader.ts rename to libs/config/src/loaders/env.loader.ts diff --git a/libs/config-new/src/loaders/file.loader.ts b/libs/config/src/loaders/file.loader.ts similarity index 100% rename from libs/config-new/src/loaders/file.loader.ts rename to libs/config/src/loaders/file.loader.ts diff --git a/libs/config/src/logging.ts b/libs/config/src/logging.ts deleted file mode 100644 index 4bfcfd9..0000000 --- a/libs/config/src/logging.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Logging configuration using Yup - * Application logging settings without Loki (Loki config is in monitoring.ts) - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, bool, num, strWithChoices } = envValidators; - -/** - * Logging configuration with validation and defaults - */ -export const loggingConfig = cleanEnv(process.env, { - // Basic Logging Settings - LOG_LEVEL: strWithChoices(['debug', 'info', 'warn', 'error'], 'info', 'Logging level'), - LOG_FORMAT: strWithChoices(['json', 'simple', 'combined'], 'json', 'Log output format'), - LOG_CONSOLE: bool(true, 'Enable console logging'), - LOG_FILE: bool(false, 'Enable file logging'), - - // File Logging Settings - LOG_FILE_PATH: str('logs', 'Log file directory path'), - LOG_FILE_MAX_SIZE: str('20m', 'Maximum log file size'), - LOG_FILE_MAX_FILES: num(14, 'Maximum number of log files to keep'), - LOG_FILE_DATE_PATTERN: str('YYYY-MM-DD', 'Log file date pattern'), - - // Error Logging - LOG_ERROR_FILE: bool(true, 'Enable separate error log file'), - LOG_ERROR_STACK: bool(true, 'Include stack traces in error logs'), - - // Performance Logging - LOG_PERFORMANCE: bool(false, 'Enable performance logging'), - LOG_SQL_QUERIES: bool(false, 'Log SQL queries'), - LOG_HTTP_REQUESTS: bool(true, 'Log HTTP requests'), - - // Structured Logging - LOG_STRUCTURED: bool(true, 'Use structured logging format'), - LOG_TIMESTAMP: bool(true, 'Include timestamps in logs'), - LOG_CALLER_INFO: bool(false, 'Include caller information in logs'), - // Log Filtering - LOG_SILENT_MODULES: str('', 'Comma-separated list of modules to silence'), - LOG_VERBOSE_MODULES: str('', 'Comma-separated list of modules for verbose logging'), - - // Application Context - LOG_SERVICE_NAME: str('stock-bot', 'Service name for log context'), - LOG_SERVICE_VERSION: str('1.0.0', 'Service version for log context'), - LOG_ENVIRONMENT: str('development', 'Environment for log context'), -}); - -// Export typed configuration object -export type LoggingConfig = typeof loggingConfig; - -// Export individual config values for convenience -export const { - LOG_LEVEL, - LOG_FORMAT, - LOG_CONSOLE, - LOG_FILE, - LOG_FILE_PATH, - LOG_FILE_MAX_SIZE, - LOG_FILE_MAX_FILES, - LOG_FILE_DATE_PATTERN, - LOG_ERROR_FILE, - LOG_ERROR_STACK, - LOG_PERFORMANCE, - LOG_SQL_QUERIES, - LOG_HTTP_REQUESTS, - LOG_STRUCTURED, - LOG_TIMESTAMP, - LOG_CALLER_INFO, - LOG_SILENT_MODULES, - LOG_VERBOSE_MODULES, - LOG_SERVICE_NAME, - LOG_SERVICE_VERSION, - LOG_ENVIRONMENT, -} = loggingConfig; diff --git a/libs/config/src/loki.ts b/libs/config/src/loki.ts deleted file mode 100644 index c8f9f86..0000000 --- a/libs/config/src/loki.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Loki log aggregation configuration using Yup - * Centralized logging configuration for the Stock Bot platform - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, port, bool, num } = envValidators; - -/** - * Loki configuration with validation and defaults - */ -export const lokiConfig = cleanEnv(process.env, { - // Loki Server - LOKI_HOST: str('localhost', 'Loki host'), - LOKI_PORT: port(3100, 'Loki port'), - LOKI_URL: str('', 'Complete Loki URL (overrides host/port)'), - - // Authentication - LOKI_USERNAME: str('', 'Loki username (if auth enabled)'), - LOKI_PASSWORD: str('', 'Loki password (if auth enabled)'), - LOKI_TENANT_ID: str('', 'Loki tenant ID (for multi-tenancy)'), - - // Push Configuration - LOKI_PUSH_TIMEOUT: num(10000, 'Push timeout in ms'), - LOKI_BATCH_SIZE: num(1024, 'Batch size for log entries'), - LOKI_BATCH_WAIT: num(5, 'Batch wait time in ms'), - - // Retention Settings - LOKI_RETENTION_PERIOD: str('30d', 'Log retention period'), - LOKI_MAX_CHUNK_AGE: str('1h', 'Maximum chunk age'), - - // TLS Settings - LOKI_TLS_ENABLED: bool(false, 'Enable TLS for Loki'), - LOKI_TLS_INSECURE: bool(false, 'Skip TLS verification'), - - // Log Labels - LOKI_DEFAULT_LABELS: str('', 'Default labels for all log entries (JSON format)'), - LOKI_SERVICE_LABEL: str('stock-bot', 'Service label for log entries'), - LOKI_ENVIRONMENT_LABEL: str('development', 'Environment label for log entries'), -}); - -// Export typed configuration object -export type LokiConfig = typeof lokiConfig; - -// Export individual config values for convenience -export const { - LOKI_HOST, - LOKI_PORT, - LOKI_URL, - LOKI_USERNAME, - LOKI_PASSWORD, - LOKI_TENANT_ID, - LOKI_PUSH_TIMEOUT, - LOKI_BATCH_SIZE, - LOKI_BATCH_WAIT, - LOKI_RETENTION_PERIOD, - LOKI_MAX_CHUNK_AGE, - LOKI_TLS_ENABLED, - LOKI_TLS_INSECURE, - LOKI_DEFAULT_LABELS, - LOKI_SERVICE_LABEL, - LOKI_ENVIRONMENT_LABEL, -} = lokiConfig; diff --git a/libs/config/src/mongodb.ts b/libs/config/src/mongodb.ts deleted file mode 100644 index 9e552c3..0000000 --- a/libs/config/src/mongodb.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * MongoDB configuration using Yup - * Document storage for sentiment data, raw documents, and unstructured data - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, port, bool, num, strWithChoices } = envValidators; - -/** - * MongoDB configuration with validation and defaults - */ -export const mongodbConfig = cleanEnv(process.env, { - // MongoDB Connection - MONGODB_HOST: str('localhost', 'MongoDB host'), - MONGODB_PORT: port(27017, 'MongoDB port'), - MONGODB_DATABASE: str('stock', 'MongoDB database name'), - - // Authentication - MONGODB_USERNAME: str('trading_admin', 'MongoDB username'), - MONGODB_PASSWORD: str('', 'MongoDB password'), - MONGODB_AUTH_SOURCE: str('admin', 'MongoDB authentication database'), - - // Connection URI (alternative to individual settings) - MONGODB_URI: str('', 'Complete MongoDB connection URI (overrides individual settings)'), - - // Connection Pool Settings - MONGODB_MAX_POOL_SIZE: num(10, 'Maximum connection pool size'), - MONGODB_MIN_POOL_SIZE: num(0, 'Minimum connection pool size'), - MONGODB_MAX_IDLE_TIME: num(30000, 'Maximum idle time for connections in ms'), - - // Timeouts - MONGODB_CONNECT_TIMEOUT: num(10000, 'Connection timeout in ms'), - MONGODB_SOCKET_TIMEOUT: num(30000, 'Socket timeout in ms'), - MONGODB_SERVER_SELECTION_TIMEOUT: num(5000, 'Server selection timeout in ms'), - - // SSL/TLS Settings - MONGODB_TLS: bool(false, 'Enable TLS for MongoDB connection'), - MONGODB_TLS_INSECURE: bool(false, 'Allow invalid certificates in TLS mode'), - MONGODB_TLS_CA_FILE: str('', 'Path to TLS CA certificate file'), - - // Additional Settings - MONGODB_RETRY_WRITES: bool(true, 'Enable retryable writes'), - MONGODB_JOURNAL: bool(true, 'Enable write concern journal'), - MONGODB_READ_PREFERENCE: strWithChoices( - ['primary', 'primaryPreferred', 'secondary', 'secondaryPreferred', 'nearest'], - 'primary', - 'MongoDB read preference' - ), - MONGODB_WRITE_CONCERN: str('majority', 'Write concern level'), -}); - -// Export typed configuration object -export type MongoDbConfig = typeof mongodbConfig; - -// Export individual config values for convenience -export const { - MONGODB_HOST, - MONGODB_PORT, - MONGODB_DATABASE, - MONGODB_USERNAME, - MONGODB_PASSWORD, - MONGODB_AUTH_SOURCE, - MONGODB_URI, - MONGODB_MAX_POOL_SIZE, - MONGODB_MIN_POOL_SIZE, - MONGODB_MAX_IDLE_TIME, - MONGODB_CONNECT_TIMEOUT, - MONGODB_SOCKET_TIMEOUT, - MONGODB_SERVER_SELECTION_TIMEOUT, - MONGODB_TLS, - MONGODB_TLS_INSECURE, - MONGODB_TLS_CA_FILE, - MONGODB_RETRY_WRITES, - MONGODB_JOURNAL, - MONGODB_READ_PREFERENCE, - MONGODB_WRITE_CONCERN, -} = mongodbConfig; diff --git a/libs/config/src/monitoring.ts b/libs/config/src/monitoring.ts deleted file mode 100644 index ac53bd3..0000000 --- a/libs/config/src/monitoring.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Monitoring configuration using Yup - * Prometheus metrics, Grafana visualization, and Loki logging - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, port, bool, num, strWithChoices } = envValidators; - -/** - * Prometheus configuration with validation and defaults - */ -export const prometheusConfig = cleanEnv(process.env, { - // Prometheus Server - PROMETHEUS_HOST: str('localhost', 'Prometheus host'), - PROMETHEUS_PORT: port(9090, 'Prometheus port'), - PROMETHEUS_URL: str('', 'Complete Prometheus URL (overrides host/port)'), - - // Authentication - PROMETHEUS_USERNAME: str('', 'Prometheus username (if auth enabled)'), - PROMETHEUS_PASSWORD: str('', 'Prometheus password (if auth enabled)'), - - // Metrics Collection - PROMETHEUS_SCRAPE_INTERVAL: str('15s', 'Default scrape interval'), - PROMETHEUS_EVALUATION_INTERVAL: str('15s', 'Rule evaluation interval'), - PROMETHEUS_RETENTION_TIME: str('15d', 'Data retention time'), - - // TLS Settings - PROMETHEUS_TLS_ENABLED: bool(false, 'Enable TLS for Prometheus'), - PROMETHEUS_TLS_INSECURE: bool(false, 'Skip TLS verification'), -}); - -/** - * Grafana configuration with validation and defaults - */ -export const grafanaConfig = cleanEnv(process.env, { - // Grafana Server - GRAFANA_HOST: str('localhost', 'Grafana host'), - GRAFANA_PORT: port(3000, 'Grafana port'), - GRAFANA_URL: str('', 'Complete Grafana URL (overrides host/port)'), - - // Authentication - GRAFANA_ADMIN_USER: str('admin', 'Grafana admin username'), - GRAFANA_ADMIN_PASSWORD: str('admin', 'Grafana admin password'), - - // Security Settings - GRAFANA_ALLOW_SIGN_UP: bool(false, 'Allow user sign up'), - GRAFANA_SECRET_KEY: str('', 'Grafana secret key for encryption'), - - // Database Settings - GRAFANA_DATABASE_TYPE: strWithChoices( - ['mysql', 'postgres', 'sqlite3'], - 'sqlite3', - 'Grafana database type' - ), - GRAFANA_DATABASE_URL: str('', 'Grafana database URL'), - - // Feature Flags - GRAFANA_DISABLE_GRAVATAR: bool(true, 'Disable Gravatar avatars'), - GRAFANA_ENABLE_GZIP: bool(true, 'Enable gzip compression'), -}); - -// Export typed configuration objects -export type PrometheusConfig = typeof prometheusConfig; -export type GrafanaConfig = typeof grafanaConfig; - -// Export individual config values for convenience -export const { - PROMETHEUS_HOST, - PROMETHEUS_PORT, - PROMETHEUS_URL, - PROMETHEUS_USERNAME, - PROMETHEUS_PASSWORD, - PROMETHEUS_SCRAPE_INTERVAL, - PROMETHEUS_EVALUATION_INTERVAL, - PROMETHEUS_RETENTION_TIME, - PROMETHEUS_TLS_ENABLED, - PROMETHEUS_TLS_INSECURE, -} = prometheusConfig; - -export const { - GRAFANA_HOST, - GRAFANA_PORT, - GRAFANA_URL, - GRAFANA_ADMIN_USER, - GRAFANA_ADMIN_PASSWORD, - GRAFANA_ALLOW_SIGN_UP, - GRAFANA_SECRET_KEY, - GRAFANA_DATABASE_TYPE, - GRAFANA_DATABASE_URL, - GRAFANA_DISABLE_GRAVATAR, - GRAFANA_ENABLE_GZIP, -} = grafanaConfig; diff --git a/libs/config/src/postgres.ts b/libs/config/src/postgres.ts deleted file mode 100644 index ebc8cd6..0000000 --- a/libs/config/src/postgres.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * PostgreSQL configuration using Yup - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, port, bool, num } = envValidators; - -/** - * PostgreSQL configuration with validation and defaults - */ -export const postgresConfig = cleanEnv(process.env, { - // PostgreSQL Connection Settings - POSTGRES_HOST: str('localhost', 'PostgreSQL host'), - POSTGRES_PORT: port(5432, 'PostgreSQL port'), - POSTGRES_DATABASE: str('stockbot', 'PostgreSQL database name'), - POSTGRES_USERNAME: str('stockbot', 'PostgreSQL username'), - POSTGRES_PASSWORD: str('', 'PostgreSQL password'), - - // Connection Pool Settings - POSTGRES_POOL_MIN: num(2, 'Minimum pool connections'), - POSTGRES_POOL_MAX: num(10, 'Maximum pool connections'), - POSTGRES_POOL_IDLE_TIMEOUT: num(30000, 'Pool idle timeout in ms'), - - // SSL Configuration - POSTGRES_SSL: bool(false, 'Enable SSL for PostgreSQL connection'), - POSTGRES_SSL_REJECT_UNAUTHORIZED: bool(true, 'Reject unauthorized SSL certificates'), - - // Additional Settings - POSTGRES_QUERY_TIMEOUT: num(30000, 'Query timeout in ms'), - POSTGRES_CONNECTION_TIMEOUT: num(5000, 'Connection timeout in ms'), - POSTGRES_STATEMENT_TIMEOUT: num(30000, 'Statement timeout in ms'), - POSTGRES_LOCK_TIMEOUT: num(10000, 'Lock timeout in ms'), - POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT: num(60000, 'Idle in transaction timeout in ms'), -}); - -// Export typed configuration object -export type PostgresConfig = typeof postgresConfig; - -// Export individual config values for convenience -export const { - POSTGRES_HOST, - POSTGRES_PORT, - POSTGRES_DATABASE, - POSTGRES_USERNAME, - POSTGRES_PASSWORD, - POSTGRES_POOL_MIN, - POSTGRES_POOL_MAX, - POSTGRES_POOL_IDLE_TIMEOUT, - POSTGRES_SSL, - POSTGRES_SSL_REJECT_UNAUTHORIZED, - POSTGRES_QUERY_TIMEOUT, - POSTGRES_CONNECTION_TIMEOUT, - POSTGRES_STATEMENT_TIMEOUT, - POSTGRES_LOCK_TIMEOUT, - POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT, -} = postgresConfig; diff --git a/libs/config/src/questdb.ts b/libs/config/src/questdb.ts deleted file mode 100644 index de90988..0000000 --- a/libs/config/src/questdb.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * QuestDB configuration using Yup - * Time-series database for OHLCV data, indicators, and performance metrics - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, port, bool, num } = envValidators; - -/** - * QuestDB configuration with validation and defaults - */ -export const questdbConfig = cleanEnv(process.env, { - // QuestDB Connection - QUESTDB_HOST: str('localhost', 'QuestDB host'), - QUESTDB_HTTP_PORT: port(9000, 'QuestDB HTTP port (web console)'), - QUESTDB_PG_PORT: port(8812, 'QuestDB PostgreSQL wire protocol port'), - QUESTDB_INFLUX_PORT: port(9009, 'QuestDB InfluxDB line protocol port'), - - // Authentication (if enabled) - QUESTDB_USER: str('', 'QuestDB username (if auth enabled)'), - QUESTDB_PASSWORD: str('', 'QuestDB password (if auth enabled)'), - - // Connection Settings - QUESTDB_CONNECTION_TIMEOUT: num(5000, 'Connection timeout in ms'), - QUESTDB_REQUEST_TIMEOUT: num(30000, 'Request timeout in ms'), - QUESTDB_RETRY_ATTEMPTS: num(3, 'Number of retry attempts'), - - // TLS Settings - QUESTDB_TLS_ENABLED: bool(false, 'Enable TLS for QuestDB connection'), - QUESTDB_TLS_VERIFY_SERVER_CERT: bool(true, 'Verify server certificate'), - - // Database Settings - QUESTDB_DEFAULT_DATABASE: str('qdb', 'Default database name'), - QUESTDB_TELEMETRY_ENABLED: bool(false, 'Enable telemetry'), -}); - -// Export typed configuration object -export type QuestDbConfig = typeof questdbConfig; - -// Export individual config values for convenience -export const { - QUESTDB_HOST, - QUESTDB_HTTP_PORT, - QUESTDB_PG_PORT, - QUESTDB_INFLUX_PORT, - QUESTDB_USER, - QUESTDB_PASSWORD, - QUESTDB_CONNECTION_TIMEOUT, - QUESTDB_REQUEST_TIMEOUT, - QUESTDB_RETRY_ATTEMPTS, - QUESTDB_TLS_ENABLED, - QUESTDB_TLS_VERIFY_SERVER_CERT, - QUESTDB_DEFAULT_DATABASE, - QUESTDB_TELEMETRY_ENABLED, -} = questdbConfig; diff --git a/libs/config/src/risk.ts b/libs/config/src/risk.ts deleted file mode 100644 index ce4bdaf..0000000 --- a/libs/config/src/risk.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Risk management configuration using Yup - */ -import { cleanEnv, envValidators } from './env-utils'; - -const { str, num, bool, strWithChoices } = envValidators; - -/** - * Risk configuration with validation and defaults - */ -export const riskConfig = cleanEnv(process.env, { - // Position Sizing - RISK_MAX_POSITION_SIZE: num(0.1, 'Maximum position size as percentage of portfolio'), - RISK_MAX_PORTFOLIO_EXPOSURE: num(0.8, 'Maximum portfolio exposure percentage'), - RISK_MAX_SINGLE_ASSET_EXPOSURE: num(0.2, 'Maximum exposure to single asset'), - RISK_MAX_SECTOR_EXPOSURE: num(0.3, 'Maximum exposure to single sector'), - - // Stop Loss and Take Profit - RISK_DEFAULT_STOP_LOSS: num(0.05, 'Default stop loss percentage'), - RISK_DEFAULT_TAKE_PROFIT: num(0.15, 'Default take profit percentage'), - RISK_TRAILING_STOP_ENABLED: bool(true, 'Enable trailing stop losses'), - RISK_TRAILING_STOP_DISTANCE: num(0.03, 'Trailing stop distance percentage'), - - // Risk Limits - RISK_MAX_DAILY_LOSS: num(0.05, 'Maximum daily loss percentage'), - RISK_MAX_WEEKLY_LOSS: num(0.1, 'Maximum weekly loss percentage'), - RISK_MAX_MONTHLY_LOSS: num(0.2, 'Maximum monthly loss percentage'), - - // Volatility Controls - RISK_MAX_VOLATILITY_THRESHOLD: num(0.4, 'Maximum volatility threshold'), - RISK_VOLATILITY_LOOKBACK_DAYS: num(20, 'Volatility calculation lookback period'), - - // Correlation Controls - RISK_MAX_CORRELATION_THRESHOLD: num(0.7, 'Maximum correlation between positions'), - RISK_CORRELATION_LOOKBACK_DAYS: num(60, 'Correlation calculation lookback period'), - - // Leverage Controls - RISK_MAX_LEVERAGE: num(2.0, 'Maximum leverage allowed'), - RISK_MARGIN_CALL_THRESHOLD: num(0.3, 'Margin call threshold'), - - // Circuit Breakers - RISK_CIRCUIT_BREAKER_ENABLED: bool(true, 'Enable circuit breakers'), - RISK_CIRCUIT_BREAKER_LOSS_THRESHOLD: num(0.1, 'Circuit breaker loss threshold'), - RISK_CIRCUIT_BREAKER_COOLDOWN_MINUTES: num(60, 'Circuit breaker cooldown period'), - - // Risk Model - RISK_MODEL_TYPE: strWithChoices(['var', 'cvar', 'expected_shortfall'], 'var', 'Risk model type'), - RISK_CONFIDENCE_LEVEL: num(0.95, 'Risk model confidence level'), - RISK_TIME_HORIZON_DAYS: num(1, 'Risk time horizon in days'), -}); - -// Export typed configuration object -export type RiskConfig = typeof riskConfig; - -// Export individual config values for convenience -export const { - RISK_MAX_POSITION_SIZE, - RISK_MAX_PORTFOLIO_EXPOSURE, - RISK_MAX_SINGLE_ASSET_EXPOSURE, - RISK_MAX_SECTOR_EXPOSURE, - RISK_DEFAULT_STOP_LOSS, - RISK_DEFAULT_TAKE_PROFIT, - RISK_TRAILING_STOP_ENABLED, - RISK_TRAILING_STOP_DISTANCE, - RISK_MAX_DAILY_LOSS, - RISK_MAX_WEEKLY_LOSS, - RISK_MAX_MONTHLY_LOSS, - RISK_MAX_VOLATILITY_THRESHOLD, - RISK_VOLATILITY_LOOKBACK_DAYS, - RISK_MAX_CORRELATION_THRESHOLD, - RISK_CORRELATION_LOOKBACK_DAYS, - RISK_MAX_LEVERAGE, - RISK_MARGIN_CALL_THRESHOLD, - RISK_CIRCUIT_BREAKER_ENABLED, - RISK_CIRCUIT_BREAKER_LOSS_THRESHOLD, - RISK_CIRCUIT_BREAKER_COOLDOWN_MINUTES, - RISK_MODEL_TYPE, - RISK_CONFIDENCE_LEVEL, - RISK_TIME_HORIZON_DAYS, -} = riskConfig; diff --git a/libs/config-new/src/schemas/base.schema.ts b/libs/config/src/schemas/base.schema.ts similarity index 100% rename from libs/config-new/src/schemas/base.schema.ts rename to libs/config/src/schemas/base.schema.ts diff --git a/libs/config-new/src/schemas/database.schema.ts b/libs/config/src/schemas/database.schema.ts similarity index 100% rename from libs/config-new/src/schemas/database.schema.ts rename to libs/config/src/schemas/database.schema.ts diff --git a/libs/config-new/src/schemas/index.ts b/libs/config/src/schemas/index.ts similarity index 100% rename from libs/config-new/src/schemas/index.ts rename to libs/config/src/schemas/index.ts diff --git a/libs/config-new/src/schemas/provider.schema.ts b/libs/config/src/schemas/provider.schema.ts similarity index 100% rename from libs/config-new/src/schemas/provider.schema.ts rename to libs/config/src/schemas/provider.schema.ts diff --git a/libs/config-new/src/schemas/service.schema.ts b/libs/config/src/schemas/service.schema.ts similarity index 100% rename from libs/config-new/src/schemas/service.schema.ts rename to libs/config/src/schemas/service.schema.ts diff --git a/libs/config-new/src/types/index.ts b/libs/config/src/types/index.ts similarity index 100% rename from libs/config-new/src/types/index.ts rename to libs/config/src/types/index.ts diff --git a/libs/config-new/src/utils/secrets.ts b/libs/config/src/utils/secrets.ts similarity index 100% rename from libs/config-new/src/utils/secrets.ts rename to libs/config/src/utils/secrets.ts diff --git a/libs/config-new/src/utils/validation.ts b/libs/config/src/utils/validation.ts similarity index 100% rename from libs/config-new/src/utils/validation.ts rename to libs/config/src/utils/validation.ts diff --git a/libs/config/test-config.mjs b/libs/config/test-config.mjs deleted file mode 100644 index df8ac0f..0000000 --- a/libs/config/test-config.mjs +++ /dev/null @@ -1,85 +0,0 @@ -import { - databaseConfig, - questdbConfig, - mongodbConfig, - dragonflyConfig, - prometheusConfig, - grafanaConfig, - lokiConfig, - loggingConfig -} from './dist/index'; - -// Set test environment variables -process.env.NODE_ENV = 'test'; -process.env.PORT = '3001'; - -// Database configs -process.env.DB_HOST = 'localhost'; -process.env.DB_PORT = '5432'; -process.env.DB_NAME = 'test_db'; -process.env.DB_USER = 'test_user'; -process.env.DB_PASSWORD = 'test_pass'; - -// QuestDB configs -process.env.QUESTDB_HOST = 'localhost'; -process.env.QUESTDB_HTTP_PORT = '9000'; -process.env.QUESTDB_PG_PORT = '8812'; - -// MongoDB configs -process.env.MONGODB_HOST = 'localhost'; -process.env.MONGODB_PORT = '27017'; -process.env.MONGODB_DATABASE = 'test_db'; - -// Dragonfly configs -process.env.DRAGONFLY_HOST = 'localhost'; -process.env.DRAGONFLY_PORT = '6379'; - -// Monitoring configs -process.env.PROMETHEUS_HOST = 'localhost'; -process.env.PROMETHEUS_PORT = '9090'; -process.env.GRAFANA_HOST = 'localhost'; -process.env.GRAFANA_PORT = '3000'; - -// Loki configs -process.env.LOKI_HOST = 'localhost'; -process.env.LOKI_PORT = '3100'; - -// Logging configs -process.env.LOG_LEVEL = 'info'; -process.env.LOG_FORMAT = 'json'; - -console.log('šŸ” Testing configuration modules...\n'); - -const configs = [ - { name: 'Database', config: databaseConfig }, - { name: 'QuestDB', config: questdbConfig }, - { name: 'MongoDB', config: mongodbConfig }, - { name: 'Dragonfly', config: dragonflyConfig }, - { name: 'Prometheus', config: prometheusConfig }, - { name: 'Grafana', config: grafanaConfig }, - { name: 'Loki', config: lokiConfig }, - { name: 'Logging', config: loggingConfig }, -]; - -let successful = 0; - -for (const { name, config } of configs) { - try { - if (config && typeof config === 'object' && Object.keys(config).length > 0) { - console.log(`āœ… ${name}: Loaded successfully`); - successful++; - } else { - console.log(`āŒ ${name}: Invalid config object`); - } - } catch (error) { - console.log(`āŒ ${name}: ${error.message}`); - } -} - -console.log(`\nšŸ“Š Test Summary: ${successful}/${configs.length} modules loaded successfully`); - -if (successful === configs.length) { - console.log('šŸŽ‰ All configuration modules working correctly!'); -} else { - console.log('āš ļø Some configuration modules have issues.'); -} diff --git a/libs/config-new/test/config-manager.test.ts b/libs/config/test/config-manager.test.ts similarity index 100% rename from libs/config-new/test/config-manager.test.ts rename to libs/config/test/config-manager.test.ts diff --git a/libs/config-new/test/index.test.ts b/libs/config/test/index.test.ts similarity index 100% rename from libs/config-new/test/index.test.ts rename to libs/config/test/index.test.ts diff --git a/libs/config/test/integration.test.ts b/libs/config/test/integration.test.ts deleted file mode 100644 index e13d853..0000000 --- a/libs/config/test/integration.test.ts +++ /dev/null @@ -1,445 +0,0 @@ -/** - * Integration Tests for Config Library - * - * Tests the entire configuration system including module interactions, - * environment loading, validation across modules, and type exports. - */ - -import { beforeEach, describe, expect, test } from 'bun:test'; -import { clearEnvVars, getMinimalTestEnv, setTestEnv } from '../test/setup'; - -describe('Config Library Integration', () => { - beforeEach(() => { - // Clear module cache for clean state - // Note: Bun handles module caching differently than Jest - }); - - describe('Complete Configuration Loading', () => { - test('should load all configuration modules successfully', async () => { - setTestEnv(getMinimalTestEnv()); - // Import all modules - const [ - { Environment, getEnvironment }, - { postgresConfig }, - { questdbConfig }, - { mongodbConfig }, - { loggingConfig }, - { riskConfig }, - ] = await Promise.all([ - import('../src/core'), - import('../src/postgres'), - import('../src/questdb'), - import('../src/mongodb'), - import('../src/logging'), - import('../src/risk'), - ]); - - // Verify all configs are loaded - expect(Environment).toBeDefined(); - expect(getEnvironment).toBeDefined(); - expect(postgresConfig).toBeDefined(); - expect(questdbConfig).toBeDefined(); - expect(mongodbConfig).toBeDefined(); - expect(loggingConfig).toBeDefined(); - expect(riskConfig).toBeDefined(); - // Verify core utilities - expect(getEnvironment()).toBe(Environment.Testing); // Should be Testing due to NODE_ENV=test in setup - expect(postgresConfig.POSTGRES_HOST).toBe('localhost'); - expect(questdbConfig.QUESTDB_HOST).toBe('localhost'); - expect(mongodbConfig.MONGODB_HOST).toBe('localhost'); // fix: use correct property - expect(loggingConfig.LOG_LEVEL).toBeDefined(); - expect(riskConfig.RISK_MAX_POSITION_SIZE).toBe(0.1); - }); - test('should handle missing required environment variables gracefully', async () => { - setTestEnv({ - NODE_ENV: 'test', - // Missing required variables - }); - - // Should be able to load core utilities - const { Environment, getEnvironment } = await import('../src/core'); - expect(Environment).toBeDefined(); - expect(getEnvironment()).toBe(Environment.Testing); - // Should fail to load modules requiring specific vars (if they have required vars) - // Note: Most modules have defaults, so they might not throw - try { - const { postgresConfig } = await import('../src/postgres'); - expect(postgresConfig).toBeDefined(); - expect(postgresConfig.POSTGRES_HOST).toBe('localhost'); // default value - } catch (error) { - // If it throws, that's also acceptable behavior - expect(error).toBeDefined(); - } - }); - test('should maintain consistency across environment detection', async () => { - setTestEnv({ - NODE_ENV: 'production', - ...getMinimalTestEnv(), - }); - const [ - { Environment, getEnvironment }, - { postgresConfig }, - { questdbConfig }, - { mongodbConfig }, - { loggingConfig }, - ] = await Promise.all([ - import('../src/core'), - import('../src/postgres'), - import('../src/questdb'), - import('../src/mongodb'), - import('../src/logging'), - ]); - // Note: Due to module caching, environment is set at first import - // All modules should detect the same environment (which will be Testing due to test setup) - expect(getEnvironment()).toBe(Environment.Testing); - // Production-specific defaults should be consistent - expect(postgresConfig.POSTGRES_SSL).toBe(false); // default is false unless overridden expect(questdbConfig.QUESTDB_TLS_ENABLED).toBe(false); // checking actual property name - expect(mongodbConfig.MONGODB_TLS).toBe(false); // checking actual property name - expect(loggingConfig.LOG_FORMAT).toBe('json'); - }); - }); - - describe('Main Index Exports', () => { - test('should export all configuration objects from index', async () => { - setTestEnv(getMinimalTestEnv()); - - const config = await import('../src/index'); - - // Core utilities (no coreConfig object) - expect(config.Environment).toBeDefined(); - expect(config.getEnvironment).toBeDefined(); - expect(config.ConfigurationError).toBeDefined(); - - // Configuration objects - expect(config.postgresConfig).toBeDefined(); - expect(config.questdbConfig).toBeDefined(); - expect(config.mongodbConfig).toBeDefined(); - expect(config.loggingConfig).toBeDefined(); - expect(config.riskConfig).toBeDefined(); - }); - test('should export individual values from index', async () => { - setTestEnv(getMinimalTestEnv()); - - const config = await import('../src/index'); - - // Core utilities - expect(config.Environment).toBeDefined(); - expect(config.getEnvironment).toBeDefined(); - - // Individual configuration values exported from modules - expect(config.POSTGRES_HOST).toBeDefined(); - expect(config.POSTGRES_PORT).toBeDefined(); - expect(config.QUESTDB_HOST).toBeDefined(); - expect(config.MONGODB_HOST).toBeDefined(); - - // Risk values - expect(config.RISK_MAX_POSITION_SIZE).toBeDefined(); - expect(config.RISK_MAX_DAILY_LOSS).toBeDefined(); - - // Logging values - expect(config.LOG_LEVEL).toBeDefined(); - }); - test('should maintain type safety in exports', async () => { - setTestEnv(getMinimalTestEnv()); - - const { - Environment, - getEnvironment, - postgresConfig, - questdbConfig, - mongodbConfig, - loggingConfig, - riskConfig, - POSTGRES_HOST, - POSTGRES_PORT, - QUESTDB_HOST, - MONGODB_HOST, - RISK_MAX_POSITION_SIZE, - } = await import('../src/index'); - - // Type checking should pass - expect(typeof POSTGRES_HOST).toBe('string'); - expect(typeof POSTGRES_PORT).toBe('number'); - expect(typeof QUESTDB_HOST).toBe('string'); - expect(typeof MONGODB_HOST).toBe('string'); - expect(typeof RISK_MAX_POSITION_SIZE).toBe('number'); - - // Configuration objects should have expected shapes - expect(postgresConfig).toHaveProperty('POSTGRES_HOST'); - expect(postgresConfig).toHaveProperty('POSTGRES_PORT'); - expect(questdbConfig).toHaveProperty('QUESTDB_HOST'); - expect(mongodbConfig).toHaveProperty('MONGODB_HOST'); - expect(loggingConfig).toHaveProperty('LOG_LEVEL'); - expect(riskConfig).toHaveProperty('RISK_MAX_POSITION_SIZE'); - }); - }); - describe('Environment Variable Validation', () => { - test('should validate environment variables across all modules', async () => { - setTestEnv({ - NODE_ENV: 'test', - LOG_LEVEL: 'info', // valid level - POSTGRES_HOST: 'localhost', - POSTGRES_DATABASE: 'test', - POSTGRES_USERNAME: 'test', - POSTGRES_PASSWORD: 'test', - QUESTDB_HOST: 'localhost', - MONGODB_HOST: 'localhost', - MONGODB_DATABASE: 'test', - RISK_MAX_POSITION_SIZE: '0.1', - RISK_MAX_DAILY_LOSS: '0.05', - }); // All imports should succeed with valid config - const [core, postgres, questdb, mongodb, logging, risk] = await Promise.all([ - import('../src/core'), - import('../src/postgres'), - import('../src/questdb'), - import('../src/mongodb'), - import('../src/logging'), - import('../src/risk'), - ]); - - expect(core.getEnvironment()).toBe(core.Environment.Testing); // default test env - expect(postgres.postgresConfig.POSTGRES_HOST).toBe('localhost'); - expect(questdb.questdbConfig.QUESTDB_HOST).toBe('localhost'); - expect(mongodb.mongodbConfig.MONGODB_HOST).toBe('localhost'); - expect(logging.loggingConfig.LOG_LEVEL).toBe('info'); // set in test - expect(risk.riskConfig.RISK_MAX_POSITION_SIZE).toBe(0.1); // from test env - }); - test('should accept valid environment variables across all modules', async () => { - setTestEnv({ - NODE_ENV: 'development', - LOG_LEVEL: 'debug', - - POSTGRES_HOST: 'localhost', - POSTGRES_PORT: '5432', - POSTGRES_DATABASE: 'stockbot_dev', - POSTGRES_USERNAME: 'dev_user', - POSTGRES_PASSWORD: 'dev_pass', - POSTGRES_SSL: 'false', - - QUESTDB_HOST: 'localhost', - QUESTDB_HTTP_PORT: '9000', - QUESTDB_PG_PORT: '8812', - - MONGODB_HOST: 'localhost', - MONGODB_DATABASE: 'stockbot_dev', - - RISK_MAX_POSITION_SIZE: '0.25', - RISK_MAX_DAILY_LOSS: '0.025', - - LOG_FORMAT: 'json', - LOG_FILE_ENABLED: 'false', - }); - - // All imports should succeed - const [core, postgres, questdb, mongodb, logging, risk] = await Promise.all([ - import('../src/core'), - import('../src/postgres'), - import('../src/questdb'), - import('../src/mongodb'), - import('../src/logging'), - import('../src/risk'), - ]); - - // Since this is the first test to set NODE_ENV to development and modules might not be cached yet, - // this could actually change the environment. Let's test what we actually get. - expect(core.getEnvironment()).toBeDefined(); // Just verify it returns something valid - expect(postgres.postgresConfig.POSTGRES_HOST).toBe('localhost'); - expect(questdb.questdbConfig.QUESTDB_HOST).toBe('localhost'); - expect(mongodb.mongodbConfig.MONGODB_HOST).toBe('localhost'); - expect(logging.loggingConfig.LOG_FORMAT).toBe('json'); // default value - expect(risk.riskConfig.RISK_MAX_POSITION_SIZE).toBe(0.1); // default value - }); - }); - - describe('Configuration Consistency', () => { - test('should maintain consistent SSL settings across databases', async () => { - setTestEnv({ - NODE_ENV: 'production', - POSTGRES_HOST: 'prod-postgres.com', - POSTGRES_DATABASE: 'prod_db', - POSTGRES_USERNAME: 'prod_user', - POSTGRES_PASSWORD: 'prod_pass', - QUESTDB_HOST: 'prod-questdb.com', - MONGODB_HOST: 'prod-mongo.com', - MONGODB_DATABASE: 'prod_db', - RISK_MAX_POSITION_SIZE: '0.1', - RISK_MAX_DAILY_LOSS: '0.05', - // SSL settings not explicitly set - should use defaults - }); - - const [postgres, questdb, mongodb] = await Promise.all([ - import('../src/postgres'), - import('../src/questdb'), - import('../src/mongodb'), - ]); - - // Check actual SSL property names and their default values expect(postgres.postgresConfig.POSTGRES_SSL).toBe(false); // default is false - expect(questdb.questdbConfig.QUESTDB_TLS_ENABLED).toBe(false); // default is false - expect(mongodb.mongodbConfig.MONGODB_TLS).toBe(false); // default is false - }); - test('should maintain consistent environment detection across modules', async () => { - setTestEnv({ - NODE_ENV: 'staging', - ...getMinimalTestEnv(), - }); - - const [core, logging] = await Promise.all([import('../src/core'), import('../src/logging')]); - expect(core.getEnvironment()).toBe(core.Environment.Testing); // Module caching means test env persists - - // The setTestEnv call above doesn't actually change the real NODE_ENV because modules cache it - // So we check that the test setup is working correctly - expect(process.env.NODE_ENV).toBe('test'); // This is what's actually set in test environment - }); - }); - - describe('Performance and Caching', () => { - test('should cache configuration values between imports', async () => { - setTestEnv(getMinimalTestEnv()); - - // Import the same module multiple times - const postgres1 = await import('../src/postgres'); - const postgres2 = await import('../src/postgres'); - const postgres3 = await import('../src/postgres'); - - // Should return the same object reference (cached) - expect(postgres1.postgresConfig).toBe(postgres2.postgresConfig); - expect(postgres2.postgresConfig).toBe(postgres3.postgresConfig); - }); - - test('should handle rapid sequential imports', async () => { - setTestEnv(getMinimalTestEnv()); - - // Import all modules simultaneously - const startTime = Date.now(); - - await Promise.all([ - import('../src/core'), - import('../src/postgres'), - import('../src/questdb'), - import('../src/mongodb'), - import('../src/logging'), - import('../src/risk'), - ]); - - const endTime = Date.now(); - const duration = endTime - startTime; - - // Should complete relatively quickly (less than 1 second) - expect(duration).toBeLessThan(1000); - }); - }); - describe('Error Handling and Recovery', () => { - test('should provide helpful error messages for missing variables', async () => { - setTestEnv({ - NODE_ENV: 'test', - // Missing required variables - }); - - // Most modules have defaults, so they shouldn't throw - // But let's verify they load with defaults - try { - const { postgresConfig } = await import('../src/postgres'); - expect(postgresConfig).toBeDefined(); - expect(postgresConfig.POSTGRES_HOST).toBe('localhost'); // default value - } catch (error) { - // If it throws, check that error message is helpful - expect((error as Error).message).toBeTruthy(); - } - - try { - const { riskConfig } = await import('../src/risk'); - expect(riskConfig).toBeDefined(); - expect(riskConfig.RISK_MAX_POSITION_SIZE).toBe(0.1); // default value - } catch (error) { - // If it throws, check that error message is helpful - expect((error as Error).message).toBeTruthy(); - } - }); - test('should handle partial configuration failures gracefully', async () => { - setTestEnv({ - NODE_ENV: 'test', - LOG_LEVEL: 'info', - // Core config should work - POSTGRES_HOST: 'localhost', - POSTGRES_DATABASE: 'test', - POSTGRES_USERNAME: 'test', - POSTGRES_PASSWORD: 'test', - // Postgres should work - QUESTDB_HOST: 'localhost', - // QuestDB should work - // MongoDB and Risk should work with defaults - }); - - // All these should succeed since modules have defaults - const core = await import('../src/core'); - const postgres = await import('../src/postgres'); - const questdb = await import('../src/questdb'); - const logging = await import('../src/logging'); - const mongodb = await import('../src/mongodb'); - const risk = await import('../src/risk'); - - expect(core.Environment).toBeDefined(); - expect(postgres.postgresConfig).toBeDefined(); - expect(questdb.questdbConfig).toBeDefined(); - expect(logging.loggingConfig).toBeDefined(); - expect(mongodb.mongodbConfig).toBeDefined(); - expect(risk.riskConfig).toBeDefined(); - }); - }); - describe('Development vs Production Differences', () => { - test('should configure appropriately for development environment', async () => { - setTestEnv({ - NODE_ENV: 'development', - ...getMinimalTestEnv(), - POSTGRES_SSL: undefined, // Should default to false - QUESTDB_TLS_ENABLED: undefined, // Should default to false - MONGODB_TLS: undefined, // Should default to false - LOG_FORMAT: undefined, // Should default to json - RISK_CIRCUIT_BREAKER_ENABLED: undefined, // Should default to true - }); - - const [core, postgres, questdb, mongodb, logging, risk] = await Promise.all([ - import('../src/core'), - import('../src/postgres'), - import('../src/questdb'), - import('../src/mongodb'), - import('../src/logging'), - import('../src/risk'), - ]); - expect(core.getEnvironment()).toBe(core.Environment.Testing); // Module caching means test env persists - expect(postgres.postgresConfig.POSTGRES_SSL).toBe(false); - expect(questdb.questdbConfig.QUESTDB_TLS_ENABLED).toBe(false); - expect(mongodb.mongodbConfig.MONGODB_TLS).toBe(false); - expect(logging.loggingConfig.LOG_FORMAT).toBe('json'); // default - expect(risk.riskConfig.RISK_CIRCUIT_BREAKER_ENABLED).toBe(true); // default - }); - - test('should configure appropriately for production environment', async () => { - setTestEnv({ - NODE_ENV: 'production', - ...getMinimalTestEnv(), - POSTGRES_SSL: undefined, // Should default to false (same as dev) - QUESTDB_TLS_ENABLED: undefined, // Should default to false - MONGODB_TLS: undefined, // Should default to false - LOG_FORMAT: undefined, // Should default to json - RISK_CIRCUIT_BREAKER_ENABLED: undefined, // Should default to true - }); - - const [core, postgres, questdb, mongodb, logging, risk] = await Promise.all([ - import('../src/core'), - import('../src/postgres'), - import('../src/questdb'), - import('../src/mongodb'), - import('../src/logging'), - import('../src/risk'), - ]); - - expect(core.getEnvironment()).toBe(core.Environment.Testing); // Module caching means test env persists - expect(postgres.postgresConfig.POSTGRES_SSL).toBe(false); // default doesn't change by env - expect(questdb.questdbConfig.QUESTDB_TLS_ENABLED).toBe(false); - expect(mongodb.mongodbConfig.MONGODB_TLS).toBe(false); - expect(logging.loggingConfig.LOG_FORMAT).toBe('json'); - expect(risk.riskConfig.RISK_CIRCUIT_BREAKER_ENABLED).toBe(true); - }); - }); -}); diff --git a/libs/config-new/test/loaders.test.ts b/libs/config/test/loaders.test.ts similarity index 100% rename from libs/config-new/test/loaders.test.ts rename to libs/config/test/loaders.test.ts diff --git a/libs/config/test/setup.ts b/libs/config/test/setup.ts deleted file mode 100644 index 75eeec4..0000000 --- a/libs/config/test/setup.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Test Setup for @stock-bot/config Library - * - * Provides common setup and utilities for testing configuration modules. - */ - -// Set NODE_ENV immediately at module load time -process.env.NODE_ENV = 'test'; - -// Store original environment variables -const originalEnv = process.env; - -// Note: Bun provides its own test globals, no need to import from @jest/globals -beforeEach(() => { - // Reset environment variables to original state - process.env = { ...originalEnv }; - // Ensure NODE_ENV is set to test by default - process.env.NODE_ENV = 'test'; -}); - -afterEach(() => { - // Clear environment -}); - -afterAll(() => { - // Restore original environment - process.env = originalEnv; -}); - -/** - * Helper function to set environment variables for testing - */ -export function setTestEnv(vars: Record): void { - Object.assign(process.env, vars); -} - -/** - * Helper function to clear specific environment variables - */ -export function clearEnvVars(vars: string[]): void { - vars.forEach(varName => { - delete process.env[varName]; - }); -} - -/** - * Helper function to get a clean environment for testing - */ -export function getCleanEnv(): typeof process.env { - return { - NODE_ENV: 'test', - }; -} - -/** - * Helper function to create minimal required environment variables - */ -export function getMinimalTestEnv(): Record { - return { - NODE_ENV: 'test', - // Logging - LOG_LEVEL: 'info', // Changed from 'error' to 'info' to match test expectations - // Database - POSTGRES_HOST: 'localhost', - POSTGRES_PORT: '5432', - POSTGRES_DATABASE: 'test_db', - POSTGRES_USERNAME: 'test_user', - POSTGRES_PASSWORD: 'test_pass', - // QuestDB - QUESTDB_HOST: 'localhost', - QUESTDB_HTTP_PORT: '9000', - QUESTDB_PG_PORT: '8812', - // MongoDB - MONGODB_HOST: 'localhost', - MONGODB_PORT: '27017', - MONGODB_DATABASE: 'test_db', - MONGODB_USERNAME: 'test_user', - MONGODB_PASSWORD: 'test_pass', - // Dragonfly - DRAGONFLY_HOST: 'localhost', - DRAGONFLY_PORT: '6379', - // Monitoring - PROMETHEUS_PORT: '9090', - GRAFANA_PORT: '3000', - // Data Providers - DATA_PROVIDER_API_KEY: 'test_key', - // Risk - RISK_MAX_POSITION_SIZE: '0.1', - RISK_MAX_DAILY_LOSS: '0.05', - // Admin - ADMIN_PORT: '8080', - }; -} diff --git a/libs/config/tsconfig.json b/libs/config/tsconfig.json index 768e89d..64c38b3 100644 --- a/libs/config/tsconfig.json +++ b/libs/config/tsconfig.json @@ -1,17 +1,22 @@ { - "extends": "../../tsconfig.json", "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022"], + "moduleResolution": "bundler", + "types": ["bun-types"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, "outDir": "./dist", - "rootDir": "./src" + "rootDir": "./src", + "declarationDir": "./dist", + "composite": true, + "tsBuildInfoFile": "./tsconfig.tsbuildinfo" }, "include": ["src/**/*"], - "exclude": [ - "node_modules", - "dist", - "**/*.test.ts", - "**/*.spec.ts", - "**/test/**/*", - "**/tests/**/*" - ], - "references": [{ "path": "../types" }] -} + "exclude": ["node_modules", "dist", "test"] +} \ No newline at end of file diff --git a/libs/config/turbo.json b/libs/config/turbo.json deleted file mode 100644 index 91571de..0000000 --- a/libs/config/turbo.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "build": { - "dependsOn": ["@stock-bot/types#build"], - "outputs": ["dist/**"], - "inputs": [ - "src/**", - "package.json", - "tsconfig.json", - "!**/*.test.ts", - "!**/*.spec.ts", - "!**/test/**", - "!**/tests/**", - "!**/__tests__/**" - ] - } - } -} diff --git a/libs/config/validate-config.js b/libs/config/validate-config.js deleted file mode 100644 index 075d35d..0000000 --- a/libs/config/validate-config.js +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env node - -/** - * Configuration Validation Script - * Tests that all configuration modules can be loaded and validated - */ - -// Set test environment variables -process.env.NODE_ENV = 'test'; -process.env.PORT = '3001'; - -// Database configs -process.env.DB_HOST = 'localhost'; -process.env.DB_PORT = '5432'; -process.env.DB_NAME = 'test_db'; -process.env.DB_USER = 'test_user'; -process.env.DB_PASSWORD = 'test_pass'; - -// QuestDB configs -process.env.QUESTDB_HOST = 'localhost'; -process.env.QUESTDB_HTTP_PORT = '9000'; -process.env.QUESTDB_PG_PORT = '8812'; - -// MongoDB configs -process.env.MONGODB_HOST = 'localhost'; -process.env.MONGODB_PORT = '27017'; -process.env.MONGODB_DATABASE = 'test_db'; - -// Dragonfly configs -process.env.DRAGONFLY_HOST = 'localhost'; -process.env.DRAGONFLY_PORT = '6379'; - -// Monitoring configs -process.env.PROMETHEUS_HOST = 'localhost'; -process.env.PROMETHEUS_PORT = '9090'; -process.env.GRAFANA_HOST = 'localhost'; -process.env.GRAFANA_PORT = '3000'; - -// Loki configs -process.env.LOKI_HOST = 'localhost'; -process.env.LOKI_PORT = '3100'; - -// Logging configs -process.env.LOG_LEVEL = 'info'; -process.env.LOG_FORMAT = 'json'; - -try { - console.log('šŸ” Validating configuration modules...\n'); - - // Test each configuration module - const modules = [ - { name: 'Database', path: './dist/database.js' }, - { name: 'QuestDB', path: './dist/questdb.js' }, - { name: 'MongoDB', path: './dist/mongodb.js' }, - { name: 'Dragonfly', path: './dist/dragonfly.js' }, - { name: 'Monitoring', path: './dist/monitoring.js' }, - { name: 'Loki', path: './dist/loki.js' }, - { name: 'Logging', path: './dist/logging.js' }, - ]; - - const results = []; - - for (const module of modules) { - try { - const config = require(module.path); - const configKeys = Object.keys(config); - - if (configKeys.length === 0) { - throw new Error('No exported configuration found'); - } - - // Try to access the main config object - const mainConfig = config[configKeys[0]]; - if (!mainConfig || typeof mainConfig !== 'object') { - throw new Error('Invalid configuration object'); - } - - console.log(`āœ… ${module.name}: ${configKeys.length} config(s) loaded`); - results.push({ name: module.name, status: 'success', configs: configKeys }); - - } catch (error) { - console.log(`āŒ ${module.name}: ${error.message}`); - results.push({ name: module.name, status: 'error', error: error.message }); - } - } - - // Test main index exports - try { - const indexExports = require('./dist/index.js'); - const exportCount = Object.keys(indexExports).length; - console.log(`\nāœ… Index exports: ${exportCount} modules exported`); - results.push({ name: 'Index', status: 'success', exports: exportCount }); - } catch (error) { - console.log(`\nāŒ Index exports: ${error.message}`); - results.push({ name: 'Index', status: 'error', error: error.message }); - } - - // Summary - const successful = results.filter(r => r.status === 'success').length; - const total = results.length; - - console.log(`\nšŸ“Š Validation Summary:`); - console.log(` Total modules: ${total}`); - console.log(` Successful: ${successful}`); - console.log(` Failed: ${total - successful}`); - - if (successful === total) { - console.log('\nšŸŽ‰ All configuration modules validated successfully!'); - process.exit(0); - } else { - console.log('\nāš ļø Some configuration modules failed validation.'); - process.exit(1); - } - -} catch (error) { - console.error('āŒ Validation script failed:', error.message); - process.exit(1); -} diff --git a/libs/queue/src/batch-processor.ts b/libs/queue/src/batch-processor.ts index 92895be..7a3ce1d 100644 --- a/libs/queue/src/batch-processor.ts +++ b/libs/queue/src/batch-processor.ts @@ -1,15 +1,16 @@ import { CacheProvider, createCache } from '@stock-bot/cache'; import { getLogger } from '@stock-bot/logger'; -import type { QueueManager } from './queue-manager'; +import type { Queue } from './queue-instance'; import type { BatchJobData, BatchResult, JobData, ProcessOptions } from './types'; const logger = getLogger('batch-processor'); const cacheProviders = new Map(); -function getCache(queueName: string): CacheProvider { +function getCache(queueName: string, redisConfig: any): CacheProvider { if (!cacheProviders.has(queueName)) { const cacheProvider = createCache({ + redisConfig, keyPrefix: `batch:${queueName}:`, ttl: 86400, // 24 hours default enableMetrics: true, @@ -23,11 +24,12 @@ function getCache(queueName: string): CacheProvider { * Initialize the batch cache before any batch operations * This should be called during application startup */ -export async function initializeBatchCache(queueManager: QueueManager): Promise { - const queueName = queueManager.getQueueName(); +export async function initializeBatchCache(queue: Queue): Promise { + const queueName = queue.getName(); + const redisConfig = queue.getRedisConfig(); logger.info('Initializing batch cache...', { queueName }); - const cache = getCache(queueName); + const cache = getCache(queueName, redisConfig); await cache.waitForReady(10000); logger.info('Batch cache initialized successfully', { queueName }); } @@ -38,7 +40,7 @@ export async function initializeBatchCache(queueManager: QueueManager): Promise< */ export async function processItems( items: T[], - queue: QueueManager, + queue: Queue, options: ProcessOptions ): Promise { const startTime = Date.now(); @@ -83,7 +85,7 @@ export async function processItems( */ async function processDirect( items: T[], - queue: QueueManager, + queue: Queue, options: ProcessOptions ): Promise> { const totalDelayMs = options.totalDelayHours * 60 * 60 * 1000; // Convert hours to milliseconds @@ -126,7 +128,7 @@ async function processDirect( */ async function processBatched( items: T[], - queue: QueueManager, + queue: Queue, options: ProcessOptions ): Promise> { const batchSize = options.batchSize || 100; @@ -186,7 +188,7 @@ async function processBatched( */ export async function processBatchJob( jobData: BatchJobData, - queue: QueueManager + queue: Queue ): Promise { const { payloadKey, batchIndex, totalBatches, itemCount } = jobData; @@ -250,14 +252,14 @@ function createBatches(items: T[], batchSize: number): T[][] { async function storeItems( items: T[], - queue: QueueManager, + queue: Queue, options: ProcessOptions ): Promise { if (!queue) { throw new Error('Batch cache not initialized. Call initializeBatchCache() first.'); } - const cache = getCache(queue.getQueueName()); + const cache = getCache(queue.getName(), queue.getRedisConfig()); const payloadKey = `payload:${Date.now()}:${Math.random().toString(36).substr(2, 9)}`; const payload = { @@ -280,7 +282,7 @@ async function storeItems( async function loadPayload( key: string, - queue: QueueManager + queue: Queue ): Promise<{ items: T[]; options: { @@ -295,7 +297,7 @@ async function loadPayload( throw new Error('Batch cache not initialized. Call initializeBatchCache() first.'); } - const cache = getCache(queue.getQueueName()); + const cache = getCache(queue.getName(), queue.getRedisConfig()); return (await cache.get(key)) as { items: T[]; options: { @@ -308,17 +310,17 @@ async function loadPayload( } | null; } -async function cleanupPayload(key: string, queue: QueueManager): Promise { +async function cleanupPayload(key: string, queue: Queue): Promise { if (!queue) { throw new Error('Batch cache not initialized. Call initializeBatchCache() first.'); } - const cache = getCache(queue.getQueueName()); + const cache = getCache(queue.getName(), queue.getRedisConfig()); await cache.del(key); } async function addJobsInChunks( - queue: QueueManager, + queue: Queue, jobs: Array<{ name: string; data: JobData; opts?: Record }>, chunkSize = 100 ): Promise { diff --git a/libs/queue/src/index.ts b/libs/queue/src/index.ts index fa4ea41..923a338 100644 --- a/libs/queue/src/index.ts +++ b/libs/queue/src/index.ts @@ -1,15 +1,28 @@ export * from './batch-processor'; export * from './provider-registry'; export * from './queue-manager'; +export * from './queue-instance'; +export * from './queue-factory'; export * from './types'; // Re-export commonly used functions export { initializeBatchCache, processBatchJob, processItems } from './batch-processor'; export { QueueManager } from './queue-manager'; +export { Queue } from './queue-instance'; export { providerRegistry } from './provider-registry'; +// Re-export queue factory functions +export { + initializeQueueSystem, + getQueue, + processItemsWithQueue, + getActiveQueueNames, + getQueueManager, + shutdownAllQueues +} from './queue-factory'; + // Re-export types for convenience export type { BatchResult, diff --git a/libs/queue/src/queue-factory.ts b/libs/queue/src/queue-factory.ts new file mode 100644 index 0000000..7d423ca --- /dev/null +++ b/libs/queue/src/queue-factory.ts @@ -0,0 +1,112 @@ +import { getLogger } from '@stock-bot/logger'; +import { QueueManager } from './queue-manager'; +import { Queue } from './queue-instance'; +import type { ProcessOptions, BatchResult } from './types'; + +const logger = getLogger('queue-factory'); + +// Global queue manager (manages workers and providers) +let queueManager: QueueManager | null = null; +// Registry of individual queues +const queues = new Map(); +let globalRedisConfig: any = null; + +/** + * Initialize the queue system with global configuration + */ +export async function initializeQueueSystem(config: { + redis: any; + defaultJobOptions?: any; + workers?: number; + concurrency?: number; +}): Promise { + logger.info('Initializing global queue system...'); + + globalRedisConfig = config.redis; + + // Initialize the global queue manager for worker management + queueManager = new QueueManager({ + queueName: 'system-queue-manager', + redis: globalRedisConfig, + workers: config.workers || 5, + concurrency: config.concurrency || 20, + defaultJobOptions: config.defaultJobOptions, + providers: [], // Will be set by individual services + }); + + await queueManager.initialize(); + + logger.info('Queue system initialized'); +} + +/** + * Get or create a queue for the given queue name + */ +export function getQueue(queueName: string): Queue { + if (!globalRedisConfig) { + throw new Error('Queue system not initialized. Call initializeQueueSystem() first.'); + } + + if (!queues.has(queueName)) { + logger.info(`Creating new queue: ${queueName}`); + + const queue = new Queue(queueName, globalRedisConfig); + queues.set(queueName, queue); + } + + return queues.get(queueName)!; +} + +/** + * Process items using the specified queue + */ +export async function processItemsWithQueue( + queueName: string, + items: T[], + options: ProcessOptions +): Promise { + const queue = getQueue(queueName); + return queue.processItems(items, options); +} + +/** + * Get all active queue names + */ +export function getActiveQueueNames(): string[] { + return Array.from(queues.keys()); +} + +/** + * Get the global queue manager (for advanced operations) + */ +export function getQueueManager(): QueueManager { + if (!queueManager) { + throw new Error('Queue system not initialized. Call initializeQueueSystem() first.'); + } + return queueManager; +} + +/** + * Shutdown all queues and the queue manager + */ +export async function shutdownAllQueues(): Promise { + logger.info('Shutting down all queues...'); + + // Shutdown individual queues + const queueShutdownPromises = Array.from(queues.values()).map(queue => + queue.shutdown().catch(error => { + logger.error('Error shutting down queue', { error }); + }) + ); + + await Promise.all(queueShutdownPromises); + queues.clear(); + + // Shutdown the global queue manager + if (queueManager) { + await queueManager.shutdown(); + queueManager = null; + } + + logger.info('All queues shut down'); +} \ No newline at end of file diff --git a/libs/queue/src/queue-instance.ts b/libs/queue/src/queue-instance.ts new file mode 100644 index 0000000..9cb68fe --- /dev/null +++ b/libs/queue/src/queue-instance.ts @@ -0,0 +1,186 @@ +import { Queue as BullQueue, Worker, QueueEvents, type Job } from 'bullmq'; +import { getLogger } from '@stock-bot/logger'; +import { processItems } from './batch-processor'; +import type { JobData, ProcessOptions, BatchResult } from './types'; + +const logger = getLogger('queue-instance'); + +export class Queue { + private bullQueue: BullQueue; + private workers: Worker[] = []; + private queueEvents: QueueEvents; + private queueName: string; + private redisConfig: any; + private initialized = false; + + constructor(queueName: string, redisConfig: any) { + this.queueName = queueName; + this.redisConfig = redisConfig; + + const connection = { + host: redisConfig.host, + port: redisConfig.port, + password: redisConfig.password, + db: redisConfig.db, + }; + + // Initialize BullMQ queue + this.bullQueue = new BullQueue(`{${queueName}}`, { + connection, + defaultJobOptions: { + removeOnComplete: 10, + removeOnFail: 5, + attempts: 3, + backoff: { + type: 'exponential', + delay: 1000, + }, + }, + }); + + // Initialize queue events + this.queueEvents = new QueueEvents(`{${queueName}}`, { connection }); + } + + /** + * Get the queue name + */ + getName(): string { + return this.queueName; + } + + /** + * Get the redis configuration + */ + getRedisConfig(): any { + return this.redisConfig; + } + + /** + * Initialize batch cache for this queue + */ + async initialize(): Promise { + if (this.initialized) { + return; + } + + const { initializeBatchCache } = await import('./batch-processor'); + await initializeBatchCache(this); + this.initialized = true; + + logger.info(`Queue initialized: ${this.queueName}`); + } + + /** + * Process items using this queue + */ + async processItems(items: T[], options: ProcessOptions): Promise { + // Ensure queue is initialized + if (!this.initialized) { + await this.initialize(); + } + + return processItems(items, this, options); + } + + /** + * Add a single job to the queue + */ + async add(name: string, data: JobData, options: Record = {}): Promise { + return await this.bullQueue.add(name, data, options); + } + + /** + * Add multiple jobs to the queue in bulk + */ + async addBulk( + jobs: Array<{ name: string; data: JobData; opts?: Record }> + ): Promise { + return await this.bullQueue.addBulk(jobs); + } + + /** + * Get queue statistics + */ + async getStats(): Promise<{ + waiting: number; + active: number; + completed: number; + failed: number; + delayed: number; + }> { + const [waiting, active, completed, failed, delayed] = await Promise.all([ + this.bullQueue.getWaiting(), + this.bullQueue.getActive(), + this.bullQueue.getCompleted(), + this.bullQueue.getFailed(), + this.bullQueue.getDelayed(), + ]); + + return { + waiting: waiting.length, + active: active.length, + completed: completed.length, + failed: failed.length, + delayed: delayed.length, + }; + } + + /** + * Pause the queue + */ + async pause(): Promise { + await this.bullQueue.pause(); + logger.info(`Queue paused: ${this.queueName}`); + } + + /** + * Resume the queue + */ + async resume(): Promise { + await this.bullQueue.resume(); + logger.info(`Queue resumed: ${this.queueName}`); + } + + /** + * Clean completed and failed jobs + */ + async clean(grace: number = 0, limit: number = 100): Promise { + await Promise.all([ + this.bullQueue.clean(grace, limit, 'completed'), + this.bullQueue.clean(grace, limit, 'failed'), + ]); + logger.info(`Queue cleaned: ${this.queueName}`, { grace, limit }); + } + + /** + * Shutdown this queue + */ + async shutdown(): Promise { + logger.info(`Shutting down queue: ${this.queueName}`); + + try { + // Close workers + await Promise.all(this.workers.map(worker => worker.close())); + this.workers = []; + + // Close queue events + await this.queueEvents.close(); + + // Close queue + await this.bullQueue.close(); + + logger.info(`Queue shutdown complete: ${this.queueName}`); + } catch (error) { + logger.error(`Error during queue shutdown: ${this.queueName}`, { error }); + throw error; + } + } + + /** + * Get the BullMQ queue instance (for advanced operations) + */ + getBullQueue(): BullQueue { + return this.bullQueue; + } +} \ No newline at end of file diff --git a/libs/queue/src/queue-manager.ts b/libs/queue/src/queue-manager.ts index 5174490..85db012 100644 --- a/libs/queue/src/queue-manager.ts +++ b/libs/queue/src/queue-manager.ts @@ -285,6 +285,13 @@ export class QueueManager { return this.config.queueName; } + /** + * Get the redis configuration + */ + getRedisConfig(): any { + return this.config.redis; + } + /** * Shutdown the queue manager */ diff --git a/scripts/build-libs.sh b/scripts/build-libs.sh index 2f4146d..fb92491 100755 --- a/scripts/build-libs.sh +++ b/scripts/build-libs.sh @@ -33,7 +33,7 @@ trap cleanup EXIT libs=( "types" # Base types - no dependencies "config" # Configuration - depends on types - "config-new" # Configuration - depends on types + "config" # Configuration - depends on types "logger" # Logging utilities - depends on types "utils" # Utilities - depends on types and config # Database clients diff --git a/tsconfig.json b/tsconfig.json index c9e384d..9cb2bcf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -60,9 +60,7 @@ // Applications { "path": "./apps/data-service" }, - { "path": "./apps/execution-service" }, - { "path": "./apps/portfolio-service" }, - { "path": "./apps/processing-service" }, - { "path": "./apps/strategy-service" } + { "path": "./apps/data-sync-service" }, + { "path": "./apps/web-api" } ] }