feat: introduce core-events package with event bus, hooks, and a web showcase demo

This commit is contained in:
Firman Ramdhani
2026-05-28 11:20:35 +07:00
parent 3621f837f6
commit 6fde65f04e
21 changed files with 2021 additions and 7 deletions
+67
View File
@@ -0,0 +1,67 @@
import mitt from 'mitt';
import type { AppEvents } from './events.registry';
// ─── Singleton Event Bus ────────────────────────────────────────
/**
* Application-wide event bus — a strictly typed `mitt` instance.
*
* Prefer the `publish` / `subscribe` helper functions or the React
* hooks (`useAppEvent`, `usePublishEvent`) over using this directly.
* Direct access is provided for edge cases like middleware or testing.
*
* @example
* ```ts
* import { eventBus } from '@repo/core-events';
* eventBus.on('APP:ERROR', (e) => console.error(e.message));
* ```
*/
export const eventBus = mitt<AppEvents>();
// ─── Type-Safe Helpers ──────────────────────────────────────────
/**
* Emit (publish) a typed event to all subscribers.
*
* @param type - The event name from `AppEvents`.
* @param event - The payload matching that event's type.
*
* @example
* ```ts
* publish('AUTH:PROFILE_UPDATED', { id: '1', name: 'Firman', ... });
* ```
*/
export function publish<K extends keyof AppEvents>(
type: K,
event: AppEvents[K],
): void {
eventBus.emit(type, event);
}
/**
* Subscribe to a typed event.
*
* Returns an `unsubscribe` function — call it to remove the handler.
* For React components, prefer `useAppEvent` which handles cleanup
* automatically on unmount.
*
* @param type - The event name from `AppEvents`.
* @param handler - Callback receiving the typed payload.
* @returns A function that removes this subscription.
*
* @example
* ```ts
* const unsub = subscribe('WS:STOCK_UPDATE', (data) => {
* console.log(data.price); // fully typed
* });
* // Later:
* unsub();
* ```
*/
export function subscribe<K extends keyof AppEvents>(
type: K,
handler: (event: AppEvents[K]) => void,
): () => void {
eventBus.on(type, handler);
return () => eventBus.off(type, handler);
}