A lightweight, dependency-free TypeScript library for monitoring network connectivity, latency, API health, and WebSocket status in web applications.
- π Network Connectivity - Detect online/offline status
- β‘ Latency Monitoring - Measure network speed and detect slow connections
- π₯ API Health Checks - Monitor multiple API endpoints with retry logic
- π WebSocket Monitoring - Track WebSocket connection state
- π UI Alerts - Built-in Toast, Banner, and Modal notifications
- βοΈ React & Vue - First-class framework integrations
- π Plugin System - Extend functionality with custom plugins
- π Metrics & History - Track uptime, downtime, and connection quality
- πΎ State Persistence - Maintain state across page reloads
- βΏ Accessible - Full ARIA support and keyboard navigation
- π¦ Tiny Bundle - < 8KB gzipped, zero dependencies
- π― TypeScript - Full type safety with strict mode
npm install signal-status-jsyarn add signal-status-jspnpm add signal-status-jsimport { SignalStatusWatcher } from "signal-status-js";
const watcher = new SignalStatusWatcher({
healthCheckUrl: "/api/health",
latencyThreshold: 500,
});
watcher.on("offline", () => {
console.log("Connection lost!");
});
watcher.on("slow_connection", ({ latency }) => {
console.log(`Slow connection: ${latency}ms`);
});
watcher.start();import { SignalStatusWatcher } from "signal-status-js";
import { useSignalStatus } from "signal-status-js/react";
import { useMemo } from "react";
function App() {
const watcher = useMemo(
() =>
new SignalStatusWatcher({
healthCheckUrl: "/api/health",
}),
[]
);
const status = useSignalStatus(watcher);
return (
<div>
{!status.online && <div>You are offline</div>}
{status.slow && <div>Slow connection detected</div>}
{status.apiDown && <div>Service unavailable</div>}
</div>
);
}<template>
<div>
<div v-if="!status.online">You are offline</div>
<div v-if="status.slow">Slow connection detected</div>
<div v-if="status.apiDown">Service unavailable</div>
</div>
</template>
<script setup>
import { SignalStatusWatcher } from "signal-status-js";
import { useSignalStatus } from "signal-status-js/vue";
import { onMounted } from "vue";
const watcher = new SignalStatusWatcher({
healthCheckUrl: "/api/health",
});
const status = useSignalStatus(watcher);
onMounted(() => {
watcher.start();
});
</script>interface SignalStatusConfig {
// Network Monitoring
pingUrl?: string; // Default: 'https://www.google.com/favicon.ico'
pingInterval?: number; // Default: 30000 (30s)
latencyThreshold?: number; // Default: 800 (ms)
// API Health Checks
healthCheckUrl?: string; // Single API endpoint
healthChecks?: HealthCheckConfig[]; // Multiple API endpoints
healthCheckInterval?: number; // Default: 10000 (10s)
healthCheckValidator?: (response: Response) => boolean | Promise<boolean>;
// Retry Logic
maxRetries?: number; // Default: 5
retryDelay?: number; // Default: 1000 (ms)
// WebSocket
websocket?: WebSocket; // WebSocket instance to monitor
// History & Metrics
historySize?: number; // Default: 100
// Persistence
persist?: boolean; // Default: false
persistKey?: string; // Default: 'signal-status-state'
// UI Alerts
alerts?: AlertConfig;
// Misc
silent?: boolean; // Default: false
}// Connectivity Events
watcher.on("online", ({ timestamp }) => {});
watcher.on("offline", ({ timestamp }) => {});
// Latency Events
watcher.on("slow_connection", ({ latency, threshold, timestamp }) => {});
// API Health Events
watcher.on("api_down", ({ apiId, statusCode, error, timestamp }) => {});
watcher.on("api_up", ({ apiId, responseTime, timestamp }) => {});
watcher.on("maintenance", ({ apiId, data, timestamp }) => {});
// WebSocket Events
watcher.on("ws_disconnected", ({ error, timestamp }) => {});
watcher.on("ws_reconnected", ({ timestamp }) => {});
// Network Info Events
watcher.on(
"connection_type_changed",
({ type, effectiveType, timestamp }) => {}
);// Toast Notifications
watcher.enableAlerts({
type: "toast",
position: "top-right",
duration: 5000,
messages: {
offline: "You are offline",
online: "Connection restored",
slow: "Slow connection detected",
api_down: "Service unavailable",
api_up: "Service restored",
},
});
// Banner Alerts
watcher.enableAlerts({
type: "banner",
position: "top",
messages: {
offline: "β οΈ You are offline. Working in local mode.",
},
});
// Modal Alerts
watcher.enableAlerts({
type: "modal",
messages: {
maintenance: "System under maintenance. We'll be back soon.",
},
});const watcher = new SignalStatusWatcher({
healthChecks: [
{
id: "auth-api",
url: "/api/auth/health",
interval: 15000,
},
{
id: "data-api",
url: "/api/data/health",
interval: 10000,
},
{
id: "payment-api",
url: "/api/payment/health",
interval: 20000,
},
],
});
watcher.on("api_down", ({ apiId, statusCode }) => {
console.log(`API ${apiId} is down (${statusCode})`);
});// Get metrics
const metrics = watcher.getMetrics();
console.log({
uptimePercentage: metrics.uptimePercentage,
totalDowntime: metrics.totalDowntime,
averageLatency: metrics.averageLatency,
slowConnectionCount: metrics.slowConnectionCount,
apiDownCount: metrics.apiDownCount,
});
// Get history
const history = watcher.getHistory();
history.forEach((entry) => {
console.log(`${entry.event} at ${new Date(entry.timestamp)}`);
});const analyticsPlugin = {
name: "analytics",
onEvent(event, payload, watcher) {
// Send events to analytics
gtag("event", event, payload);
},
};
watcher.registerPlugin(analyticsPlugin);const watcher = new SignalStatusWatcher({
healthChecks: [
{ id: "payment", url: "/api/payment/health" },
{ id: "inventory", url: "/api/inventory/health" },
],
});
watcher.on("api_down", ({ apiId }) => {
if (apiId === "payment") {
disableCheckoutButton();
showMessage("Payment service temporarily unavailable");
}
});const ws = new WebSocket("wss://chat.example.com");
const watcher = new SignalStatusWatcher({
websocket: ws,
healthCheckUrl: "/api/chat/health",
});
watcher.on("ws_disconnected", () => {
showReconnectingIndicator();
disableMessageInput();
});
watcher.on("ws_reconnected", () => {
hideReconnectingIndicator();
enableMessageInput();
loadMissedMessages();
});const watcher = new SignalStatusWatcher({
persist: true,
healthCheckUrl: "/api/health",
});
watcher.on("offline", () => {
// Enable service worker cache
navigator.serviceWorker.controller?.postMessage({
type: "ENABLE_OFFLINE_MODE",
});
});
watcher.on("online", () => {
// Sync data in background
navigator.serviceWorker.controller?.postMessage({
type: "SYNC_DATA",
});
});| Package | Size (gzipped) |
|---|---|
| Core | 7.41 KB |
| React | +0.34 KB |
| Vue | +0.33 KB |
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Type check
npm run type-check
# Build
npm run buildContributions are welcome! Please read our Contributing Guide for details.
MIT Β© Dominique Kossi
Give a βοΈ if this project helped you!
Made with β€οΈ by Dominique Kossi