import { config, ensureDataDirs, runsApi, runsWorker } from "./config.js"; import { logger } from "./logger.js"; import { migrate } from "./db/migrate.js"; import { pool } from "./db/pool.js"; import { seed } from "./seed/index.js"; import { instruments } from "./core/instruments.js"; import { calendar } from "./core/calendar.js"; import { quoteStore } from "./core/quotes.js"; import { eventEngine } from "./core/events.js"; import { health } from "./core/health.js"; import { syncRegistry } from "./core/registry.js"; import { connectorManager } from "./core/connector-manager.js"; import { scheduler } from "./core/jobs.js"; import { observationWriter } from "./core/observations.js"; import { barAggregator } from "./core/bars.js"; import { rawArchive } from "./core/raw-archive.js"; import { buildServer } from "./api/server.js"; import { rowToExchange } from "./core/instruments.js"; async function loadCalendar() { const [ex, hol] = await Promise.all([pool.query("select * from exchanges"), pool.query("select exchange_id, to_char(date, 'YYYY-MM-DD') as date, name, kind, close_time, source_id from exchange_holidays")]); calendar.load( ex.rows.map(rowToExchange), hol.rows.map((h) => ({ exchangeId: h.exchange_id, date: h.date, name: h.name, kind: h.kind, closeTime: h.close_time, sourceId: h.source_id })), ); } async function main() { logger.info({ env: config.appEnv, role: config.role, version: config.version }, "Market Atlas starting"); ensureDataDirs(); if (config.isProd && !config.adminToken) throw new Error("MA_ADMIN_TOKEN is required in production"); await migrate(); await seed(); await loadCalendar(); await instruments.load(); await quoteStore.load(); await eventEngine.warm(); if (runsWorker()) { await syncRegistry(); await health.hydrate(); scheduler.start(); await connectorManager.startAll(); } let app: Awaited> | null = null; if (runsApi()) { app = await buildServer(); await app.listen({ host: config.apiHost, port: config.apiPort }); logger.info({ host: config.apiHost, port: config.apiPort }, "API listening"); } let shuttingDown = false; const shutdown = async (signal: string) => { if (shuttingDown) return; shuttingDown = true; logger.info({ signal }, "shutting down"); const force = setTimeout(() => process.exit(1), 10_000); try { scheduler.stop(); await connectorManager.stopAll(); await app?.close(); await observationWriter.flush(); await quoteStore.flush(); await barAggregator.flush(); await eventEngine.flush(); rawArchive.flush(); await pool.end(); } catch (err) { logger.error({ err }, "shutdown error"); } finally { clearTimeout(force); process.exit(0); } }; process.on("SIGINT", () => void shutdown("SIGINT")); process.on("SIGTERM", () => void shutdown("SIGTERM")); process.on("unhandledRejection", (err) => logger.error({ err }, "unhandled rejection")); process.on("uncaughtException", (err) => { logger.fatal({ err }, "uncaught exception"); void shutdown("uncaughtException"); }); } main().catch((err) => { logger.fatal({ err }, "startup failed"); process.exit(1); });