fix: add type safety and error handling to event bus

- Add DomainEventPayloads interface to events.ts for typed event payloads
- Wrap dashboard listeners with fireAndForget() to prevent unhandled promise rejections
- Type all listener parameters explicitly using DomainEventPayloads
- Add idempotency guard to registerDomainEventListeners to prevent double registration on hot-reload

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
syntaxbullet
2026-03-18 13:04:32 +01:00
parent 5863418ae9
commit 0142508eb5
2 changed files with 66 additions and 33 deletions

View File

@@ -1,4 +1,5 @@
import { EventEmitter } from "node:events";
import type { Transaction } from "@shared/lib/types";
/**
* Global system event bus for cross-module communication.
@@ -46,3 +47,15 @@ export const EVENTS = {
EXAM_PASSED: "domain:exam_passed",
},
} as const;
export interface DomainEventPayloads {
[EVENTS.DOMAIN.BALANCE_CHANGED]: { userId: string; type: string; tx: Transaction };
[EVENTS.DOMAIN.XP_GAINED]: { userId: string; amount: number; tx: Transaction };
[EVENTS.DOMAIN.ITEM_COLLECTED]: { userId: string; itemId: number; quantity: number; tx: Transaction };
[EVENTS.DOMAIN.ITEM_USED]: { userId: string; itemId: number; tx: Transaction };
[EVENTS.DOMAIN.TRANSFER_COMPLETED]: { username: string; amount: bigint; toUserId: string };
[EVENTS.DOMAIN.DAILY_CLAIMED]: { username: string; amount: bigint };
[EVENTS.DOMAIN.TRIVIA_STARTED]: { username: string; difficulty: string };
[EVENTS.DOMAIN.TRIVIA_WON]: { username: string | undefined; reward: bigint };
[EVENTS.DOMAIN.EXAM_PASSED]: { username: string; reward: bigint };
}