pushstream-client
v0.2.0
Published
Lightweight JavaScript client for PushStream SSE events with auto-reconnection and event-driven API
Maintainers
Readme
pushstream-client
A lightweight, zero-dependency JavaScript client for consuming Server-Sent Events (SSE) with automatic reconnection, event subscriptions, and connection state management.
Features
- Zero dependencies - Pure JavaScript, no external libraries
- Tiny footprint - Less than 5KB minified
- TypeScript support - Full type definitions included
- Auto-reconnection - Exponential backoff with jitter to prevent thundering herd
- Event-driven API - Familiar
on()/off()subscription pattern - JSON parsing - Automatic payload parsing
- Connection state - Track connection status with built-in events
- Universal - Works in browsers and Node.js
Installation
npm / yarn / pnpm
npm install pushstream-client
# or
yarn add pushstream-client
# or
pnpm add pushstream-clientCDN / Script Tag
<script src="https://unpkg.com/pushstream-client/dist/pushstream.min.js"></script>
<script>
const client = new PushStream.EventClient('/events');
</script>Quick Start
import { EventClient } from 'pushstream-client';
// Create client
const client = new EventClient('/events');
// Subscribe to events
client.on('task.progress', (data) => {
console.log(`Task ${data.taskId}: ${data.percentage}%`);
});
client.on('task.complete', (data) => {
console.log(`Task ${data.taskId} completed!`);
});
// Handle errors
client.on('stream.error', (error) => {
console.error('Connection error:', error.message);
});
// Connect
client.connect();
// Later: disconnect
client.disconnect();TypeScript
Full TypeScript support is included out of the box. No additional @types packages needed.
import { EventClient, ConnectionState, BuiltInEvents } from 'pushstream-client';
// Define your event payload types
interface OrderUpdate {
orderId: string;
status: 'pending' | 'processing' | 'shipped' | 'delivered';
timestamp: string;
}
interface TaskProgress {
taskId: string;
percentage: number;
}
const client = new EventClient('/events', {
reconnect: true,
maxReconnectAttempts: 5
});
// Generic type parameter for type-safe callbacks
client.on<OrderUpdate>('order.updated', (data) => {
console.log(data.orderId); // string
console.log(data.status); // 'pending' | 'processing' | 'shipped' | 'delivered'
});
client.on<TaskProgress>('task.progress', (data) => {
console.log(data.percentage); // number
});
// Built-in events are also typed
client.on('stream.error', (error) => {
console.error(error.message);
});
client.connect();Available Types
import {
// Classes
EventClient,
SubscriptionManager,
// Types
EventClientOptions,
EventCallback,
ConnectionStateValue,
// Constants
ConnectionState,
BuiltInEvents,
DefaultOptions,
// Built-in event data types
StreamOpenEvent,
StreamCloseEvent,
StreamErrorEvent,
StreamStateChangeEvent
} from 'pushstream-client';API Reference
new EventClient(url, options?)
Creates a new EventClient instance.
Parameters:
url(string) - The SSE endpoint URL (relative or absolute)options(object, optional):reconnect(boolean, default:true) - Enable automatic reconnectionreconnectInterval(number, default:1000) - Base delay in millisecondsmaxReconnectAttempts(number, default:10) - Maximum retry attemptsmaxReconnectDelay(number, default:30000) - Maximum backoff delaywithCredentials(boolean, default:false) - Include cookies in CORS requests
const client = new EventClient('/events', {
reconnect: true,
reconnectInterval: 2000,
maxReconnectAttempts: 5
});connect()
Establish an SSE connection to the server. This method is idempotent - calling it while already connected has no effect.
client.connect();disconnect()
Close the SSE connection. After calling disconnect(), no automatic reconnection will be attempted.
client.disconnect();on(event, callback)
Subscribe to an event. Returns the client instance for chaining.
client
.on('task.progress', handleProgress)
.on('task.complete', handleComplete);off(event, callback?)
Unsubscribe from an event. If callback is omitted, removes all listeners for that event.
// Remove specific callback
client.off('task.progress', handleProgress);
// Remove all callbacks for event
client.off('task.progress');state (property)
Get the current connection state.
console.log(client.state); // 'disconnected' | 'connecting' | 'connected'Built-in Events
| Event | Description | Payload |
|-------|-------------|---------|
| stream.open | Connection established | { url: string } |
| stream.close | Connection closed | { manual: boolean } |
| stream.error | Error occurred | { message: string, ... } |
| stream.statechange | State changed | { previousState, currentState } |
client.on('stream.open', () => {
console.log('Connected!');
});
client.on('stream.close', ({ manual }) => {
console.log(manual ? 'Disconnected by user' : 'Connection lost');
});
client.on('stream.error', ({ message }) => {
console.error('Error:', message);
});
client.on('stream.statechange', ({ previousState, currentState }) => {
console.log(`State: ${previousState} -> ${currentState}`);
});Connection States
| State | Description |
|-------|-------------|
| disconnected | Not connected to server |
| connecting | Attempting to establish connection |
| connected | Connected and receiving events |
Reconnection Behavior
By default, the client automatically reconnects when the connection is lost:
- Uses exponential backoff: delays increase with each failed attempt
- Adds jitter (random delay) to prevent all clients reconnecting simultaneously
- Respects max attempts: stops after
maxReconnectAttemptsfailures - Preserves subscriptions: no need to re-register event handlers after reconnection
To disable auto-reconnection:
const client = new EventClient('/events', { reconnect: false });Authentication
Since EventSource cannot send custom headers, use query parameters for authentication:
const client = new EventClient('/events?token=your-jwt-token');
client.connect();For cookie-based auth with CORS:
const client = new EventClient('https://api.example.com/events', {
withCredentials: true
});Browser Support
| Browser | Version | |---------|---------| | Chrome | 60+ | | Firefox | 55+ | | Safari | 11+ | | Edge | 79+ |
Node.js Support
For Node.js 18+, use with an EventSource polyfill:
import { EventSource } from 'eventsource';
globalThis.EventSource = EventSource;
import { EventClient } from 'pushstream-client';
// ... use as normalExamples
Progress Tracking
const client = new EventClient('/api/upload/events?uploadId=abc123');
client.on('upload.progress', ({ percentage, bytesUploaded }) => {
updateProgressBar(percentage);
});
client.on('upload.complete', ({ fileUrl }) => {
showSuccess(fileUrl);
client.disconnect();
});
client.on('upload.error', ({ message }) => {
showError(message);
client.disconnect();
});
client.connect();Live Notifications
const client = new EventClient('/api/notifications');
client.on('notification', ({ title, body, type }) => {
showNotification(title, body, type);
});
client.on('stream.error', () => {
showOfflineIndicator();
});
client.on('stream.open', () => {
hideOfflineIndicator();
});
client.connect();TypeScript
TypeScript definitions are planned for a future release. For now, you can create a basic .d.ts file:
declare module 'pushstream-client' {
export class EventClient {
constructor(url: string, options?: EventClientOptions);
connect(): void;
disconnect(): void;
on(event: string, callback: (data: any) => void): this;
off(event: string, callback?: (data: any) => void): this;
readonly state: 'disconnected' | 'connecting' | 'connected';
}
export interface EventClientOptions {
reconnect?: boolean;
reconnectInterval?: number;
maxReconnectAttempts?: number;
maxReconnectDelay?: number;
withCredentials?: boolean;
}
}License
MIT
