EdgeStream
Connection Lifecycle
Every transport follows the same status state machine: disconnected → connecting → connected → reconnecting → connected (or error on failure). BaseTransport ensures handlers are only called on actual status changes.
Status State Machine
connect()
disconnected ─────────────────→ connecting
│
success │ failure
↓ ↓
connected error
│
drops │
↓
reconnecting
│
reconnect │ all attempts
succeeds │ exhausted
↓ ↓
connected disconnected
disconnect() from any state → disconnected
Monitoring Status Changes
const server = stream.server('bas')!;
// Subscribe to all status transitions
const unsub = server.transport.onStatusChange((status) => {
switch (status) {
case 'connecting':
console.log('[BAS] Connecting to server...');
break;
case 'connected':
console.log('[BAS] Connected');
connectionStore.setStatus('bas', 'connected');
break;
case 'reconnecting':
console.log('[BAS] Connection lost — reconnecting...');
connectionStore.setStatus('bas', 'reconnecting');
break;
case 'error':
console.error('[BAS] Connection error:', server.transport.lastError?.message);
connectionStore.setStatus('bas', 'error');
break;
case 'disconnected':
console.log('[BAS] Disconnected');
connectionStore.setStatus('bas', 'disconnected');
break;
}
});
// Unsubscribe when done
unsub(); // remove the handler
Per-Transport Lifecycle Details
| Event | SignalR | WebSocket | SSE | HTTP Polling |
|---|---|---|---|---|
connecting | start() called | new WebSocket() | new EventSource() | poll loop starts |
connected | onreconnected or start resolves | ws.onopen | eventSource.open | first successful GET |
reconnecting | onreconnecting callback | ws.onclose + retry | eventSource error + closed | consecutive GET failures |
error | start() rejects | ws.onerror | eventSource error (not closed) | unrecoverable fetch error |
disconnected | onclose (after stop) | disconnect() called | disconnect() called | disconnect() called |
lastError Property
// Access the last error that occurred on the transport
const transport = stream.server('bas')!.transport;
if (transport.status === 'error') {
console.error('Last error:', transport.lastError?.message);
// e.g. 'WebSocket connection timeout'
// e.g. 'SignalR connection not connected'
// e.g. 'HTTP 401: Unauthorized'
}
Error Monitoring
server.transport.onError((error) => {
// Called whenever a transport-level error occurs
// This is separate from status — status may still be 'reconnecting'
// while errors are being logged
metrics.increment('edge_stream.transport_error', { server: 'bas' });
errorMonitor.capture(error, { transport: 'signalr', server: 'bas' });
});
Idempotent Connect and Disconnect
// connect() is idempotent — calling when already connected is a no-op
await transport.connect(); // connects
await transport.connect(); // returns immediately — already connected
// disconnect() is idempotent — calling when already disconnected is a no-op
await transport.disconnect(); // disconnects
await transport.disconnect(); // returns immediately
BaseTransport Deduplication
setStatus() in BaseTransport only fires handlers when the status actually changes. If a reconnect attempt emits 'reconnecting' repeatedly, handlers only receive one call for that status — not one per retry attempt.