/** * Backend telemetry singleton — wraps @bytelyst/telemetry-client for Node.js. * * Uses a Map-based storage adapter in place of localStorage (browser/RN default). * All tracking calls are fire-and-forget; this module never throws. * * Initialise once in bootstrap.ts after secrets are resolved: * import { tradingTelemetry } from './services/tradingTelemetry.js'; * tradingTelemetry.init(); */ import { createTelemetryClient } from '@bytelyst/telemetry-client'; import type { TelemetryClient, TelemetryStorage } from '@bytelyst/telemetry-client'; import { config } from '../config/index.js'; import { productConfig } from '../../../shared/product.js'; // --------------------------------------------------------------------------- // Node.js storage adapter — Map replaces localStorage // --------------------------------------------------------------------------- const nodeStorage: TelemetryStorage = (() => { const store = new Map(); return { getItem: (key: string) => store.get(key) ?? null, setItem: (key: string, value: string) => { store.set(key, value); }, }; })(); // --------------------------------------------------------------------------- // Singleton client // --------------------------------------------------------------------------- const client: TelemetryClient = createTelemetryClient({ productId: productConfig.productId, baseUrl: config.PLATFORM_API_URL, platform: 'backend', channel: 'invttrdg_backend', transport: 'fetch', appVersion: productConfig.version, releaseChannel: process.env.NODE_ENV === 'production' ? 'production' : 'dev', osFamily: 'node', osVersion: process.version, storage: nodeStorage, // Flush every 60s — matches the trading loop polling interval flushIntervalMs: 60_000, maxQueue: 100, }); // --------------------------------------------------------------------------- // Public API // --------------------------------------------------------------------------- /** Initialise telemetry and start periodic flushing. Call once at startup. */ function init(): void { try { client.init(); } catch { // Non-fatal — telemetry must not prevent trading from starting } } /** Gracefully flush remaining events and stop the flush timer. */ async function shutdown(): Promise { try { client.shutdown(); } catch { // Ignore — we're shutting down anyway } } /** * Track a backend telemetry event. Never throws. * * @param eventType Severity/class: 'info' | 'error' | 'lifecycle' | 'perf' * @param module Source module label, e.g. 'trading_loop', 'trade_executor' * @param eventName Specific event, e.g. 'cycle_complete', 'order_filled' * @param extra Optional extra fields (userId, tags, metrics, message) */ function trackEvent( eventType: string, module: string, eventName: string, extra?: { feature?: string; message?: string; tags?: Record; metrics?: Record; userId?: string; }, ): void { try { client.trackEvent(eventType, module, eventName, extra); } catch { // Non-fatal } } export const tradingTelemetry = { init, shutdown, trackEvent };