{"version":3,"file":"event-bus.mjs","names":[],"sources":["../../../src/services/event-bus.ts"],"sourcesContent":["/**\n * Event Bus — typed pub/sub for internal plan-engine events.\n *\n * Lightweight, zero-dependency event emitter for decoupling trigger sources\n * (price watcher, cron evaluator) from the scheduler. Events are fire-and-forget\n * (handlers run async but errors don't propagate).\n *\n * Usage:\n *   const bus = getEventBus();\n *   bus.on('price_crossed', handler);\n *   bus.emit('price_crossed', { token: 'ETH', ... });\n */\n\n// ─── Event Types ────────────────────────────────────────────────────────\n\nexport interface PriceCrossedEvent {\n  type: 'price_crossed';\n  token: string;\n  condition: 'above' | 'below' | 'crosses';\n  threshold: number;\n  currentPrice: number;\n  previousPrice: number;\n  timestamp: number;\n}\n\nexport interface CronTickEvent {\n  type: 'cron_tick';\n  /** The cron expression that matched. */\n  expression: string;\n  /** ISO timestamp of the tick. */\n  tickTime: string;\n  timestamp: number;\n}\n\nexport interface PlanCompletedEvent {\n  type: 'plan_completed';\n  planId: string;\n  executionId: string;\n  status: 'completed' | 'failed' | 'cancelled';\n  timestamp: number;\n}\n\nexport interface CheckpointResumedEvent {\n  type: 'checkpoint_resumed';\n  planId: string;\n  executionId: string;\n  nodeId: string;\n  timestamp: number;\n}\n\nexport interface OnChainEvent {\n  type: 'onchain_event';\n  /** Chain ID where the event was detected. */\n  chainId: number;\n  /** Contract address that emitted the event. */\n  contractAddress: string;\n  /** Event signature (e.g. 'Transfer(address,address,uint256)'). */\n  eventSignature: string;\n  /** Decoded event topics. */\n  topics: string[];\n  /** Raw log data. */\n  data: string;\n  /** Block number. */\n  blockNumber: number;\n  /** Transaction hash. */\n  txHash: string;\n  timestamp: number;\n}\n\nexport interface BalanceChangedEvent {\n  type: 'balance_changed';\n  /** Token symbol or address. */\n  token: string;\n  /** Chain ID. */\n  chainId: number;\n  /** Previous balance. */\n  previousBalance: number;\n  /** Current balance. */\n  currentBalance: number;\n  /** Absolute change. */\n  change: number;\n  /** Direction of change. */\n  direction: 'increased' | 'decreased';\n  /** Wallet address. */\n  walletAddress: string;\n  timestamp: number;\n}\n\nexport interface WebhookReceivedEvent {\n  type: 'webhook_received';\n  /** Route name that matched. */\n  route: string;\n  /** Source label (e.g. \"GitHub\", \"Stripe\"). */\n  source: string;\n  /** Parsed payload body. */\n  payload: unknown;\n  /** HTTP headers from the request. */\n  headers: Record<string, string>;\n  /** When the webhook was received. */\n  receivedAt: number;\n  timestamp: number;\n}\n\nexport type BusEvent =\n  | PriceCrossedEvent\n  | CronTickEvent\n  | PlanCompletedEvent\n  | CheckpointResumedEvent\n  | OnChainEvent\n  | BalanceChangedEvent\n  | WebhookReceivedEvent;\n\nexport type BusEventType = BusEvent['type'];\n\n// Extract event by type\ntype EventOfType<T extends BusEventType> = Extract<BusEvent, { type: T }>;\n\nexport type BusEventHandler<T extends BusEventType = BusEventType> = (\n  event: EventOfType<T>,\n) => void | Promise<void>;\n\n// ─── Event Bus ──────────────────────────────────────────────────────────\n\nexport class EventBus {\n  private handlers = new Map<BusEventType, Set<BusEventHandler<any>>>();\n  private allHandlers = new Set<BusEventHandler<any>>();\n\n  /** Subscribe to a specific event type. Returns unsubscribe function. */\n  on<T extends BusEventType>(type: T, handler: BusEventHandler<T>): () => void {\n    if (!this.handlers.has(type)) {\n      this.handlers.set(type, new Set());\n    }\n    this.handlers.get(type)!.add(handler);\n    return () => {\n      this.handlers.get(type)?.delete(handler);\n    };\n  }\n\n  /** Subscribe to all events. Returns unsubscribe function. */\n  onAny(handler: BusEventHandler): () => void {\n    this.allHandlers.add(handler);\n    return () => {\n      this.allHandlers.delete(handler);\n    };\n  }\n\n  /** Emit an event to all subscribers (async, errors swallowed). */\n  emit<T extends BusEventType>(type: T, event: EventOfType<T>): void {\n    const typeHandlers = this.handlers.get(type);\n    if (typeHandlers) {\n      for (const handler of typeHandlers) {\n        try {\n          const result = handler(event);\n          if (result && typeof (result as Promise<void>).catch === 'function') {\n            (result as Promise<void>).catch(() => {});\n          }\n        } catch { /* swallow sync errors */ }\n      }\n    }\n\n    for (const handler of this.allHandlers) {\n      try {\n        const result = handler(event);\n        if (result && typeof (result as Promise<void>).catch === 'function') {\n          (result as Promise<void>).catch(() => {});\n        }\n      } catch { /* swallow */ }\n    }\n  }\n\n  /** Remove all handlers. */\n  clear(): void {\n    this.handlers.clear();\n    this.allHandlers.clear();\n  }\n\n  /** Get handler count for a specific event type (for testing). */\n  listenerCount(type?: BusEventType): number {\n    if (type) {\n      return (this.handlers.get(type)?.size ?? 0) + this.allHandlers.size;\n    }\n    let total = this.allHandlers.size;\n    for (const set of this.handlers.values()) {\n      total += set.size;\n    }\n    return total;\n  }\n}\n\n// ─── Singleton ──────────────────────────────────────────────────────────\n\nlet instance: EventBus | null = null;\n\nexport function getEventBus(): EventBus {\n  if (!instance) {\n    instance = new EventBus();\n  }\n  return instance;\n}\n\nexport function resetEventBus(): void {\n  instance?.clear();\n  instance = null;\n}\n"],"mappings":";AA2HA,IAAa,WAAb,MAAsB;CACpB,2BAAmB,IAAI,KAA8C;CACrE,8BAAsB,IAAI,KAA2B;;CAGrD,GAA2B,MAAS,SAAyC;AAC3E,MAAI,CAAC,KAAK,SAAS,IAAI,KAAK,CAC1B,MAAK,SAAS,IAAI,sBAAM,IAAI,KAAK,CAAC;AAEpC,OAAK,SAAS,IAAI,KAAK,CAAE,IAAI,QAAQ;AACrC,eAAa;AACX,QAAK,SAAS,IAAI,KAAK,EAAE,OAAO,QAAQ;;;;CAK5C,MAAM,SAAsC;AAC1C,OAAK,YAAY,IAAI,QAAQ;AAC7B,eAAa;AACX,QAAK,YAAY,OAAO,QAAQ;;;;CAKpC,KAA6B,MAAS,OAA6B;EACjE,MAAM,eAAe,KAAK,SAAS,IAAI,KAAK;AAC5C,MAAI,aACF,MAAK,MAAM,WAAW,aACpB,KAAI;GACF,MAAM,SAAS,QAAQ,MAAM;AAC7B,OAAI,UAAU,OAAQ,OAAyB,UAAU,WACtD,QAAyB,YAAY,GAAG;UAErC;AAIZ,OAAK,MAAM,WAAW,KAAK,YACzB,KAAI;GACF,MAAM,SAAS,QAAQ,MAAM;AAC7B,OAAI,UAAU,OAAQ,OAAyB,UAAU,WACtD,QAAyB,YAAY,GAAG;UAErC;;;CAKZ,QAAc;AACZ,OAAK,SAAS,OAAO;AACrB,OAAK,YAAY,OAAO;;;CAI1B,cAAc,MAA6B;AACzC,MAAI,KACF,SAAQ,KAAK,SAAS,IAAI,KAAK,EAAE,QAAQ,KAAK,KAAK,YAAY;EAEjE,IAAI,QAAQ,KAAK,YAAY;AAC7B,OAAK,MAAM,OAAO,KAAK,SAAS,QAAQ,CACtC,UAAS,IAAI;AAEf,SAAO;;;AAMX,IAAI,WAA4B;AAEhC,SAAgB,cAAwB;AACtC,KAAI,CAAC,SACH,YAAW,IAAI,UAAU;AAE3B,QAAO;;AAGT,SAAgB,gBAAsB;AACpC,WAAU,OAAO;AACjB,YAAW"}