docs: update README files for core packages with architecture diagrams and usage examples

This commit is contained in:
Firman Ramdhani
2026-05-28 12:20:53 +07:00
parent df229c9984
commit c510feadbb
4 changed files with 255 additions and 87 deletions
+41 -7
View File
@@ -89,6 +89,12 @@ declare module '@repo/core-events' {
'STORE:ORDER_PLACED': OrderPayload;
'STORE:ORDER_CANCELLED': { orderId: string; reason: string };
'UI:SIDEBAR_TOGGLED': { collapsed: boolean };
// Explicit payloads for the examples below:
'DEVICE:PRINT_RECEIPT': { receiptId: string; items: any[]; total: number; cashierName: string; timestamp: number };
'WS:STOCK_UPDATE': { id: string; price: number };
'AUTH:PROFILE_UPDATED': { id: string; name: string; email: string; avatar: string; updatedAt: number };
'SYSTEM:ERROR': { source: string; error: Error };
}
}
```
@@ -102,7 +108,7 @@ function CheckoutButton() {
const publish = usePublishEvent();
// ✅ 'STORE:ORDER_PLACED' autocompletes.
// ✅ Payload shape is enforced by TypeScript.
publish('STORE:ORDER_PLACED', { orderId: '123', total: 99, items: [...] });
publish('STORE:ORDER_PLACED', { orderId: '123', total: 99, items: [] });
}
function OrderTracker() {
@@ -124,6 +130,29 @@ function OrderTracker() {
---
## Usage Outside React (Vanilla TS)
For utility files, API interceptors, Web Workers, or vanilla functions where React hooks cannot be used, import the raw `eventBus` instance directly.
```ts
import { eventBus } from '@repo/core-events';
// Publishing
eventBus.publish('STORE:ORDER_CANCELLED', { orderId: '123', reason: 'Out of stock' });
// Subscribing
const handler = (payload) => {
console.log('Order cancelled:', payload.orderId);
};
eventBus.subscribe('STORE:ORDER_CANCELLED', handler);
// CRITICAL: Always unsubscribe when done to prevent memory leaks in non-React contexts!
eventBus.unsubscribe('STORE:ORDER_CANCELLED', handler);
```
---
## Usage Examples
Here are three real-world architectural patterns powered by the Event Bus. All event types below are registered in `apps/web/src/types/events.d.ts`, **not** in the core package.
@@ -145,7 +174,7 @@ export function CashierUI() {
// Fire and forget. Zero knowledge of how printing actually happens.
publish('DEVICE:PRINT_RECEIPT', {
receiptId: 'RCP-123',
items: [...],
items: [],
total: 45.00,
cashierName: 'Firman',
timestamp: Date.now(),
@@ -235,7 +264,7 @@ export const StockRow = memo(function StockRow({ stockId }) {
**Problem**: When a user updates their profile, we need to persist it to the secure local IndexedDB. We don't want to tightly couple our UI forms to the `@repo/core-storage` package.
**Solution**: The UI form announces the profile update. A dedicated storage listener persists it in the background.
**Solution**: The UI form announces the profile update. A dedicated storage listener persists it in the background, properly escalating errors if the storage fails.
**Publisher (Profile UI)**:
```tsx
@@ -249,7 +278,7 @@ export function ProfileSettingsUI() {
id: 'user-1',
name: 'Firman',
email: 'firman@eigen.co.id',
avatar: 'https://example.com/avatar.png',
avatar: '[https://example.com/avatar.png](https://example.com/avatar.png)',
updatedAt: Date.now(),
});
};
@@ -260,16 +289,21 @@ export function ProfileSettingsUI() {
**Subscriber (Storage Sync Listener)**:
```tsx
import { useAppEvent } from '@repo/core-events';
import { useAppEvent, usePublishEvent } from '@repo/core-events';
import { secureIndexedDB } from '@repo/core-storage';
export function StorageSyncListener() {
const publish = usePublishEvent();
useAppEvent('AUTH:PROFILE_UPDATED', (payload) => {
// Automatically encrypted at rest because 'user_profile'
// is defined in ENCRYPTED_KEYS in @repo/core-storage
secureIndexedDB.setItem('user_profile', payload).catch(console.error);
secureIndexedDB.setItem('user_profile', payload).catch((error) => {
// Escalate to global error handler instead of swallowing it
publish('SYSTEM:ERROR', { source: 'StorageSyncListener', error });
});
});
return null;
}
```
```