Package Exports
- @devforgets/pulse
Readme
@devforgets/pulse
A high-performance event system for React applications. Originally developed as an internal tool for managing complex event-driven architectures, now available as part of the DevForge toolkit.
Table of Contents
- Introduction
- Installation
- Quick Start
- Core Concepts
- Advanced Features
- DevTools
- API Reference
- Best Practices
- Real-World Examples
- Contributing
- License
Introduction
Pulse is a powerful event system designed specifically for React applications. It provides a type-safe, efficient, and developer-friendly way to handle events across your application. With built-in performance monitoring and development tools, Pulse makes it easy to manage complex event-driven architectures.
Why Pulse?
- ๐ฏ Type Safety: Full TypeScript support with extensive type inference
- โก High Performance: Optimized for React with automatic batching and cleanup
- ๐งฉ React Integration: Seamless integration with React's lifecycle and hooks
- ๐ DevTools: Built-in debugging and performance monitoring tools
- ๐งน Smart Cleanup: Automatic memory management and inactive handler cleanup
- ๐จ Developer Experience: Intuitive API with comprehensive tooling
Key Features
- Type-safe event handling
- Automatic event batching
- Priority-based handlers
- Component-aware cleanup
- Real-time performance monitoring
- Built-in developer tools
- Memory leak prevention
- React hooks integration
Installation
# Using npm
npm install @devforgets/pulse
# Using yarn
yarn add @devforgets/pulse
# Using pnpm
pnpm add @devforgets/pulse
Quick Start
import { Pulse } from '@devforgets/pulse';
// Define your events
interface AppEvents {
'user:login': { userId: string };
'data:update': { value: number };
}
// Create your pulse system
const AppPulse = Pulse.createContext<AppEvents>();
// Use in your App
function App() {
const { PulseProvider } = AppPulse;
return (
<PulseProvider>
<YourComponents />
</PulseProvider>
);
}
// Use in components
function LoginButton() {
const { useEvent, emitEvent } = AppPulse;
const emit = emitEvent();
// Listen to events
useEvent('user:login', (data) => {
console.log('User logged in:', data.userId);
});
// Emit events
const handleLogin = () => {
emit('user:login', { userId: '123' });
};
return <button onClick={handleLogin}>Login</button>;
}
With DevTools (Development Only)
import { Pulse, PulseDevTools } from '@devforgets/pulse';
function App() {
const { PulseProvider, usePulse } = AppPulse;
return (
<PulseProvider>
<YourComponents />
{process.env.NODE_ENV === 'development' && (
<PulseDevTools
pulse={usePulse()}
position="bottom-right"
theme="light"
/>
)}
</PulseProvider>
);
}
Core Concepts
Event System
Pulse uses a type-safe event system where events are defined through TypeScript interfaces. Each event has a unique name and associated data type.
interface AppEvents {
'user:login': { userId: string; timestamp: number };
'user:logout': void;
'data:update': { id: string; value: any };
}
Event Handlers
Handlers can be registered with different priorities and options:
// Basic handler
useEvent('user:login', (data) => {
console.log('User logged in:', data.userId);
});
// High priority handler
useEvent('user:login', (data) => {
console.log('First to know about login:', data.userId);
}, { priority: 10 });
// Persistent handler
useEvent('data:update', (data) => {
console.log('Data updated:', data);
}, { keepAlive: true });
Event Emission
Events can be emitted individually or batched:
function UserActions() {
const { emitEvent, batchEvents } = AppPulse;
const emit = emitEvent();
// Single event
const handleLogin = () => {
emit('user:login', {
userId: '123',
timestamp: Date.now()
});
};
// Batched events
const handleFullUpdate = () => {
batchEvents(() => {
emit('user:login', {
userId: '123',
timestamp: Date.now()
});
emit('data:update', {
id: 'user-123',
value: { status: 'active' }
});
});
};
return (
<div>
<button onClick={handleLogin}>Login</button>
<button onClick={handleFullUpdate}>Full Update</button>
</div>
);
}
Advanced Features
Priority Handlers
Handlers can be assigned priorities to ensure critical handlers execute first:
function CriticalComponent() {
const { useEvent } = AppPulse;
// High priority handler (executes first)
useEvent('data:update', (data) => {
console.log('Critical update:', data);
}, { priority: 100 });
// Normal priority handler
useEvent('data:update', (data) => {
console.log('Normal update:', data);
});
}
Event Batching
Group multiple events to optimize performance:
function DataSync() {
const { emitEvent, batchEvents } = AppPulse;
const emit = emitEvent();
const syncData = () => {
batchEvents(() => {
// All these events will be batched and processed together
emit('data:update', { id: 'user', value: userData });
emit('data:update', { id: 'settings', value: settings });
emit('data:update', { id: 'preferences', value: prefs });
});
};
}
Subscription Management
Control event subscriptions with fine-grained options:
function SubscriptionDemo() {
const { useEvent } = AppPulse;
useEffect(() => {
// Create subscription with options
const subscription = useEvent('data:update', (data) => {
console.log('Data:', data);
}, {
priority: 10,
keepAlive: true
});
// Pause/Resume subscription
subscription.pause();
subscription.resume();
// Check status
console.log('Is paused:', subscription.isPaused());
return () => subscription.unsubscribe();
}, []);
}
Component-Aware Cleanup
Pulse automatically manages subscriptions based on component lifecycle:
function AutoCleanupDemo() {
const { useEvent } = AppPulse;
// This subscription will be automatically cleaned up
// when the component unmounts
useEvent('user:login', (data) => {
console.log('User:', data);
});
// This subscription will persist even after unmount
useEvent('system:status', (data) => {
console.log('System:', data);
}, { keepAlive: true });
}
DevTools
Pulse includes a lightweight yet powerful development tool for monitoring and debugging your event system in real-time.
Setup DevTools
import { Pulse, PulseDevTools } from '@devforgets/pulse';
// Create your Pulse instance with types
const { PulseProvider, usePulse, useEvent, emitEvent } = Pulse.createContext<AppEvents>();
// Wrapper component to access Pulse instance
const DevToolsWrapper = () => {
const pulse = usePulse();
return <PulseDevTools pulseInstance={pulse} />;
};
function App() {
return (
<PulseProvider>
<YourComponents />
{process.env.NODE_ENV === 'development' && <DevToolsWrapper />}
</PulseProvider>
);
}
Features
Real-Time Event Monitoring
- Live event counter (events/second)
- Active event handlers tracking
- Memory usage monitoring
- Event history and data inspection
Event Metrics
- Total active handlers
- Memory consumption
- Event frequency charts
- Handler distribution
Event Inspection
- Real-time event logs
- Event data inspection
- Handler counts per event
- Event timing information
Visual Features
- Floating status indicator
- Expandable detailed view
- Real-time performance charts
- Event type categorization
Example with Active Monitoring
interface AppEvents {
'system:heartbeat': { timestamp: number };
'user:activity': { userId: string; action: string; timestamp: number };
'performance:update': { memory: number; eventCount: number; timestamp: number };
}
// Event Generator Component
function EventsGenerator() {
const emit = emitEvent();
useEffect(() => {
// System heartbeat
const heartbeat = setInterval(() => {
emit('system:heartbeat', { timestamp: Date.now() });
}, 1000);
// User activity simulation
const activity = setInterval(() => {
emit('user:activity', {
userId: 'user1',
action: 'interaction',
timestamp: Date.now()
});
}, 3000);
// Performance metrics
const performance = setInterval(() => {
emit('performance:update', {
memory: Math.round(window.performance.memory?.usedJSHeapSize / 1024 / 1024) || 0,
eventCount: 0,
timestamp: Date.now()
});
}, 5000);
return () => {
clearInterval(heartbeat);
clearInterval(activity);
clearInterval(performance);
};
}, [emit]);
return null;
}
Visualization Options
Metrics View
<PulseDevTools
pulseInstance={pulse}
/>
Shows:
- Events per second count
- Active handlers count
- Memory usage
- Recent events log
- Performance trends
Best Practices
- Development Only
{process.env.NODE_ENV === 'development' && <DevToolsWrapper />}
- Event Monitoring
- Use meaningful event names
- Include relevant event data
- Monitor handler counts
- Track memory usage
- Performance Optimization
- Watch for high event frequencies
- Monitor memory consumption
- Check handler cleanup
- Use event batching when needed
Integration Examples
Basic Integration
function App() {
return (
<PulseProvider>
<YourApp />
<DevToolsWrapper />
</PulseProvider>
);
}
With Real-time Monitoring
function App() {
return (
<PulseProvider>
<YourApp />
<EventsGenerator /> {/* Add continuous events */}
<DevToolsWrapper />
</PulseProvider>
);
}
The DevTools provide valuable insights into your application's event system, helping you debug, optimize, and monitor your event-driven architecture in real-time.
API Reference
Core API
Pulse.createContext<T>()
Creates a new Pulse context with typed events.
const AppPulse = Pulse.createContext<AppEvents>();
Returns:
PulseProvider
: Context provider componentusePulse
: Hook to access Pulse instanceuseEvent
: Hook to subscribe to eventsemitEvent
: Hook to emit eventsbatchEvents
: Hook to batch multiple events
Hooks
useEvent(eventName, handler, options?)
Subscribe to events with options.
useEvent('eventName', (data) => {
// Handle event
}, {
priority?: number; // Handler priority (default: 0)
keepAlive?: boolean; // Keep subscription after unmount (default: false)
disabled?: boolean; // Temporarily disable handler (default: false)
});
emitEvent()
Get function to emit events.
const emit = emitEvent();
emit('eventName', eventData);
batchEvents()
Get function to batch multiple events.
const batch = batchEvents();
batch(() => {
emit('event1', data1);
emit('event2', data2);
});
usePulse()
Access the Pulse instance directly.
const pulse = usePulse();
Types
PulseSubscription
interface PulseSubscription {
unsubscribe: () => void;
pause: () => void;
resume: () => void;
isPaused: () => boolean;
}
HandlerOptions
interface HandlerOptions {
priority?: number;
keepAlive?: boolean;
disabled?: boolean;
}
DevToolsProps
interface DevToolsProps {
pulse: Pulse<any>;
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
theme?: 'light' | 'dark';
}
Best Practices
Event Naming
Follow a consistent event naming pattern:
interface AppEvents {
// Format: 'domain:action'
'user:login': LoginData;
'user:logout': void;
// Group related events
'data:fetch': FetchParams;
'data:update': UpdateData;
'data:delete': DeleteParams;
// UI events
'ui:modal:open': ModalConfig;
'ui:modal:close': void;
'ui:theme:change': ThemeData;
}
Performance Optimization
- Use Batching for Multiple Events
// Instead of this
emit('data:update', data1);
emit('ui:refresh', {});
emit('stats:update', stats);
// Do this
batchEvents(() => {
emit('data:update', data1);
emit('ui:refresh', {});
emit('stats:update', stats);
});
- Proper Handler Cleanup
// Let Pulse handle cleanup
useEvent('data:update', handler);
// Or manage manually
useEffect(() => {
const subscription = useEvent('data:update', handler);
return () => subscription.unsubscribe();
}, []);
- Use Priority for Critical Handlers
// Critical updates first
useEvent('data:update', criticalHandler, { priority: 100 });
// Normal updates
useEvent('data:update', normalHandler, { priority: 0 });
Real-World Examples
Form Management
interface FormEvents {
'form:submit': { values: Record<string, any> };
'form:validate': { field: string; value: any };
'form:error': { field: string; error: string };
}
function FormComponent() {
const { useEvent, emitEvent } = AppPulse;
const emit = emitEvent();
const [errors, setErrors] = useState({});
// Listen for validation errors
useEvent('form:error', ({ field, error }) => {
setErrors(prev => ({ ...prev, [field]: error }));
});
const handleSubmit = (values) => {
emit('form:submit', { values });
};
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
</form>
);
}
Real-time Updates
interface RealtimeEvents {
'data:update': { path: string; value: any };
'data:sync': { timestamp: number };
'connection:status': { online: boolean };
}
function RealtimeComponent() {
const { useEvent, emitEvent } = AppPulse;
const emit = emitEvent();
// High-priority connection status
useEvent('connection:status',
({ online }) => {
console.log('Connection:', online);
},
{ priority: 100 }
);
// Regular data updates
useEvent('data:update', ({ path, value }) => {
console.log(`Update at ${path}:`, value);
});
// Periodic sync
useEffect(() => {
const interval = setInterval(() => {
emit('data:sync', { timestamp: Date.now() });
}, 5000);
return () => clearInterval(interval);
}, []);
return <div>{/* Component content */}</div>;
}
Theme Management
interface ThemeEvents {
'theme:change': { mode: 'light' | 'dark' };
'theme:update': { colors: Record<string, string> };
}
function ThemeProvider() {
const { useEvent, emitEvent } = AppPulse;
const emit = emitEvent();
// Listen for theme changes
useEvent('theme:change', ({ mode }) => {
document.documentElement.classList.toggle('dark', mode === 'dark');
});
// Listen for color updates
useEvent('theme:update', ({ colors }) => {
Object.entries(colors).forEach(([key, value]) => {
document.documentElement.style.setProperty(`--color-${key}`, value);
});
});
return <div>{/* Theme provider content */}</div>;
}
Contributing
We welcome contributions to Pulse! Here's how you can help:
Development Setup
- Clone the repository:
git clone https://github.com/devforgets/pulse.git
cd pulse
- Install dependencies:
pnpm install
- Run tests:
pnpm test
- Start development:
pnpm dev
Guidelines
Code Style
- Use TypeScript
- Follow existing patterns
- Include JSDoc comments
- Write tests for new features
Commits
- Use conventional commits
- Keep commits focused
- Include tests
- Update documentation
Pull Requests
- Create feature branch
- Add tests
- Update documentation
- Request review
Running Tests
# Run all tests
pnpm test
# Run specific tests
pnpm test:unit
pnpm test:integration
# Run with coverage
pnpm test:coverage
Building
# Build package
pnpm build
# Build documentation
pnpm docs:build
License
MIT ยฉ DevForgeTS